How to implement MDC (Material Design Component) tooltips? - javascript

I have been setting up a simple web guide without using any framework, by just using separate Material Design Components. Many of these I had no issues with (I have successfully implemented buttons with ripple effects, cards, tabs...) but I can not get tooltips to work.
Following the documentation, I have tried many things to make them appear, but they do not show.
They are included in my CSS:
#use "#material/tooltip/styles";
Instanciated in my JS:
import {MDCTooltip} from '#material/tooltip';
const tooltip = new MDCTooltip(document.querySelector('.mdc-tooltip'));
And implemented in my HTML as per the example given, first the tooltip is declared in the body:
<div id="tooltip-1" class="mdc-tooltip" role="tooltip" aria-hidden="true">
<div class="mdc-tooltip__surface mdc-tooltip__surface-animation">
TestyMcTest
</div>
</div>
...and then the tooltip is attached to another element in the same body:
<div aria-describedby="tooltip-1" class="mdc-card in-card-margins">
<div>The status of these data updates can be observed in the blabla</div>
</div>
The tooltip div is made invisible (which is expected behaviour), however, nothing shows up when mousing over the linked object (in this instance a card, but I tried with multiple types of containers and I could not get anything to work).
Thank you for any help.

Related

Angular7 component view collapsing on data manipulation

I am experiencing the strangest issue. I do have a component that's style is controlled by some object it's handling. As the object is manipulated (by some inner component click), the whole view collapses to a height of 0.
I have tried to apply the view changes via ngStyle, ngClass, plain ol'javascript - but nothing ever worked. Second strange thing - handling outer component's component styles is perfectly working.
Object
const valueMap = {
width: '100%',
...
}
Pseudo Component Tree
<component-a>
<component-b [ngStyle]="{'width': valueMap.width} [valueMap]="valueMap">
<div (click)="changePosition()">Button</div>
</component-b>
</component-a>
Method
changePosition() {
this.valueMap.width = this.valueMap.width==='100%' ? '50%' : '100%';
}
I would really expect the component-b to collapse to a width of 50%, but not having the height collapse to zero. I can see in the code, that as I click the button the style is applied to the element - and if in Chrome DevTools I deactivate the applied style and activate it again, it is showing properly. Never had something like this. Can someone please help?
Thanks in advance. André
I tried to recreate your problem. altough i couldnt get the error you seem to be having, i think i can see the issue at hand:
from your html
<component-a>
<component-b [ngStyle]="{width: 'valueMap.width'} [valueMap]="valueMap">
<div (click)="changePosition()">Button</div>
</component-b>
</component-a>
i could get, that your "valueMap" is an Input() within component-b.
so i did that on my end aswell and declared valuemap on the outermost component (the one using component a and b)
i recreated your html and found parsing errors and whatnot until i realized something:
<app-collapse-on-click>
<app-collapse-child [ngStyle]="{'width': valueMap.width, 'background-color': 'rebeccapurple'}" [valueMap]="valueMap">
<div><button (click)="changePosition()">Button</button></div>
</app-collapse-child>
</app-collapse-on-click>
when using ngStyle, the style is interpreted as an object. meaning, in your case you need to remove the ' arround valueMap.width and add them to width.
i added the background color to get a visual result other than just the style html.
hope that somehow helps.
regards
Alan

How to capture clicked element using $(this) inside of a vue(js) instance

I am reworking an old app of mine and I am having issues with dom manipulation and basic selections within a vue instance.
Essentially I have information in a database that I load in via ajax.
Each record in the db has 2 sections. The header tab(title, time, date etc) and the body of the record(notes, ideas, etc)
When loaded, the header shows normally to the user but if they want to see what that note contains, they have to click on the header for the bottom to appear.
consider the following html:
<vuejs for loop>
<div v-bind:id='item._id' class="tabW" v-on:click="blueTabClick" >
<div class="blueTabMainColor">
<!-- header stuff here -->
</div>
<div class="notesOpenedW">
<!-- interior informaton here, HIDDEN BY CSS -->
</div>
</div>
<vuejs for loop ender>
This HTML is essentially inside a Vue for/loop directive, and generates however many "tabs(tabW)" as needed based on how much info I have in the DB
All I want the user to do is to be able to click whichever tab(tabW) they want information on, and for the notes show underneath(notesOpenedW).
I stripped my entire app and js and tried to keep it as simple a test as possible and even with the below, I still can't get anything.
here is my JS(JQ):
$(document).ready(function(evt){
$(".blueTabMainColor").click(function(){
$(this).next(".notesOpenedW").fadeToggle();
});
});
With this basic code, when I put it inside a Vue instance, via:
methods: {
blueTabClick: function (evt) {
evt.preventDefault();
$(".blueTabMainColor").click(function(){
//alert("you clicked me");
$(this).next(".notesOpenedW").fadeToggle();
});
}
}
It doesn't work, but if I take it out of the Vue instance, it works just fine.
how can I get this to work? or am I going about it the wrong way?
Vue will not cohabit happily with JQuery. You're $(this) will not work because you're not even in the document at that point, you're in pure js, virtual DOM, another universe. Then, if it did, the event listener you call may not exist. You will need to fundamentally transition this code to Vue if you want it to work, I fear.
You can achieve this by setting a ref on "notesOpenedW".
https://v2.vuejs.org/v2/api/#ref
I would strongly recommend to wrap this behaviour in a dedicated component
That would have the following content :
<div class="tabW" v-on:click="blueTabClick" >
<div class="blueTabMainColor">
<!-- header stuff here -->
</div>
<div class="notesOpenedW" ref="notesToggleDiv">
<!-- interior informaton here, HIDDEN BY CSS -->
</div>
</div>
And the method :
methods: {
blueTabClick: function () {
$(this.$refs.notesToggleDiv).fadeToggle();
}
}
Be aware that when using Vue, manipulating directly the dom is usually a bad idea.
As i showed you, it is possible to use jQuery with Vue if you absolutely need it (or cannot afford to rework more deeply your application).
Edit : Just found this article that i think would help you a lot :
https://www.smashingmagazine.com/2018/02/jquery-vue-javascript/?utm_campaign=Revue%20newsletter&utm_medium=Newsletter&utm_source=Vue.js%20Developers

Vue.js - appending a slot to another component

I am trying to build a generic dropdown/popover/tooltip system in Vue.js.
The idea is to be able to open nested popover-esque elements, and manage their z layering, focus, re-focus on a child popover closing, etc. The underlying element positioning code may be the likes of Tether or Popper.
The API I want to have is this:
<div id="app">
<div class="page">
<tooltip>
<button>Hover this</button>
<div>Contents of tooltip</div>
</tooltip>
</div>
<popover-container/>
</div>
The end result DOM I want to have is:
<div id="app">
<div class="page">
<button>Hover this</button>
</div>
<div class="popover-container">
<div>Contents of tooltip</div>
</div>
</div>
It is imperative that the <tooltip/> component does not wrap the <button/> element in any additional DOM.
I have tried a number of approaches, none of which work, or at least not how I want them to.
1. Slots
I tried using slots for the <tooltip/> component. This creates a number of problems.
First, the tooltip content is appended right after the button.
Second, since you can't have multiple root nodes in a template, I had to wrap the <button/> and tooltip content in another element, which is a no-no.
I have tried the likes of vue-popper-js, which appends the tooltip content to the body, but it only happens in $nextTick, which means the template has to render with the wrapper span regardless.
2. Virtual DOM
I tried using Vue's render function. While I could render just the button, and using a standalone Vue() bus to send instructions to the <popover-container/> component to render the tooltip content, it is unable to update its content whenever the DOM is supposed to change.
Plus, there doesn't seem to be a documented way to clone VNodes, and since they are readonly, I cannot add event handlers. I hammered this together, but it's silly and wouldn't work the second my <button/> was a little more complex:
render: function (createElement) {
let slotContent = this.$slots.default[0].componentOptions;
return createElement(slotContent.Ctor, {
props: slotContent.propsData,
nativeOn: {
mouseenter: this.onMouseEnter,
...slotContent.listeners
}
}, this.$slots.default);
}
I still use slots in this case, but updates to the tooltip content from the parent component aren't doing anything here. Every time I open the tooltip, it has the old content it was created with.
Anyway, how can I do this? Is it even possible in Vue?

Checking to see if element is visible

I have a list of apps that are listed on a non angular page. The list of apps that are available depends on what subscription level was paid for. If the subscription level does not have an app purchased the app is still listed however there is an overlay over the app. (please see picture).
The html looks like this:
<div class="apps-item apps-no-border disabled">
<div class="apps-name">
<span>Interactive Event Diagrams</span>
</div>
<div class="divider">
<div class="apps-description">Interactive Event Diagrams is an indispensable online tool, allowing website visitors to view your meeting rooms and create their own customized event layouts according to their specific needs, all while using your venue’s available inventory. Users
can email and save diagrams or images for future reference.</div>
<div class="apps-image-preview">
<img alt="Interactive Event Diagrams" src="/Content/Images/AppsPreview/interactive_event_diagrams.png">
</div>
<div class="apps-action">Not Purchased</div>
<div class="overlay"></div>
</div>
</div>
Now if an app is purchased the overlay element is shaded gray in the html and is not view-able on screen. (Ex. no grey shading over Hotel Venue Explorer) I want to be able to check and see if the overlay is seen or not seen.
I've tried this:
elm = element.all(by.css('div.apps-item')).get(5);
expect(elm.$('div.overlay').isDisplayed()).toBeTruthy();
However the expect is returning false.
Other apps html, notice the grey over the overlay class
If your div.overlay is always present in the DOM, then its hard to check if its displayed because it will always be there in the DOM and your css and javascript might be handling the display property(like add overlay if its needed or don't add when its not needed). Also checking isDisplayed function for an empty html element doesn't work as far as i know.
In order to verify it you can check for css attributes that are responsible for the greying out functionality. Here's how -
elm = element.all(by.css('div.apps-item')).get(5);
//Use your css attribute that greys the apps-item div like height, width, color, etc...
expect(elm.$('div.overlay').getCssValue('background-color')).toEqual('grey');
expect(elm.$('div.overlay').getCssValue('width')).not.toBeNull(); //If you know the width then you can check for equality or greaterThan(someVal).
expect(elm.$('div.overlay').getCssValue('height')).not.toBeNull();
Hope it helps.

Zeroclipboard, triggered by any element with class="copy"

I've looked through this site along with many others and I can't see the answer anywhere.
I currently have a site with multiple buttons and a preview pane. The text shown in the preview pane differs depending on the button that the user is currently hovering over.
<body>
<div="preview_pane"> <!--ALL TEXT IS SHOWN HERE --> </div>
<div id="button_group">
<div class="copy_me" id="stock1"></div> <!--THIS SHOWS STOCK TEXT-->
<div class="copy_me" id="stock2"></div> <!--COMPLETELY DIFFERENT TEXT-->
<div class="copy_me" id="stock3"></div> <!--YET SOME OTHER DIFFERENT TEXT-->
<div class="copy_me" id="stock4"></div> <!--OTHER COMPLETELY DIFFERENT TEXT-->
</div>
</body>
What I want to do is have zeroclipboard create the flash overlay on any button with the class copy_me. All of these buttons need to copy the text shown in the preview pane.
This way when the user hovers over the button the text in the preview pane will change and then when they click, the text in the preview pane will be copied to the users clipboard.
I can't manually add the script to every button as there will be over 50 stock text buttons.
I have no experience in flash or javascript (only dabbled in jQuery) so this is something completely new for me.
Any help will be greatly appreciated.
answered a similar question at https://stackoverflow.com/a/26200988/3471658
Try using http://www.steamdev.com/zclip/ it allows you direct access to jquery and you can use your own logic in the return statement.
include jquery.zclip.js
download and save ZeroClipboard.swf
Here is a snippet:
$(".class-to-copy").zclip({
path: "assets/js/ZeroClipboard.swf",
copy: function(){
return $(this).attr("data-attribute-with-text-to-copy");
}
});
Make sure you change the path of the swf.
I looked at the api docs for zeroclipboard right quick, and I you want to use the glue method, and pass an array of dom nodes. In this case, you want all the nodes with the class name "copy_me", so:
var clip = new ZeroClipboard();
clip.glue(document.getElementsByClassName('copy_me'));
You mentioned jQuery. This should make things easier for you:
var client = new ZeroClipboard($('.copy_me'));
See:
https://github.com/zeroclipboard/zeroclipboard/blob/master/docs/instructions.md
Also see:
http://jsfiddle.net/rimian/45Nnv/

Categories