What does the class "is-upgraded" in material-design-lite do and how is it set? - material-design-lite

I'm implementing Material Design with Material-Design-lite in a React app. When I'm using TextField w/Floating Label within my top component, it works. But on a component called by by top component, it doesn't. TextField w/Floating Label then acts a regular input field.
I noticed that outer TextField gets a class named "is-upgraded", but inner TextField doesn't.
app structure
app.js
products.js
myTest.js (doesn't work)
myTest.js (works)
App.jsx
export default class App extends React.Component {
render() {
return(
<div>
<Products/>
<MyTest id="products" type="text"/>
</div>
)
}
}
Products.jsx
export default class Products extends React.Component {
render() {
return(
<div>
<List items={this.state.products}/>
<MyTest id="products" type="text"/>
<AddProduct/>
</div>
)
}
}
myTest.jsx
import React from "react";
export default class MyTest extends React.Component {
constructor(props) {
super(props)
}
render() {
return(
<div>
<div className="mdl-textfield
mdl-js-textfield
mdl-textfield--floating-label">
<input
className="mdl-textfield__input"
id={this.props.id}
type={this.props.type}/>
<label
className="mdl-textfield__label"
htmlFor={this.props.id}>
Test
</label>
</div>
</div>
)
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<base href="/">
<link href="http://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet">
<link href="https://storage.googleapis.com/code.getmdl.io/1.0.0/material.indigo-pink.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<div id="app"></div>
<script src="https://storage.googleapis.com/code.getmdl.io/1.0.0/material.min.js"> </script>
<script src="http://localhost:8080/webpack-dev-server.js"></script>
<script src="bundle.js"></script>

MDL enhances normal DOM elements by upgrading them to MDL components. In case of non-dynamic elements (i.e. everything that is already in DOM tree upon window.load), MDL automatically upgrades them upon window.load to MDL components. For everything else you have to use componentHandler to upgrade your elements manually. There as an article here which explains how to integrate React with MDL:
Integrating MDL with React is as easy as calling a single method:
componentHandler.upgradeDom();. In my opinion, the best place to put
this code is in the componentDidUpdate callback, which is invoked just
after and element has been updated and successfully rendered. This way
we can make sure that our React component already exists in the DOM
and can be upgraded by the componentHandler.
So you would have something like this:
var TestButton = React.createClass({
render: function() {
return (
<button ref="submit" className="mdl-button mdl-js-button mdl-button--raised mdl-button--accent mdl-js-ripple-effect">
Submit
</button>
);
},
componentDidUpdate: function() {
// This upgrades all upgradable components (i.e. with 'mdl-js-*' class)
componentHandler.upgradeDom();
// We could have done this manually for each component
/*
* var submitButton = this.refs.submit.getDOMNode();
* componentHandler.upgradeElement(submitButton, "MaterialButton");
* componentHandler.upgradeElement(submitButton, "MaterialRipple");
*/
}
});
Disclaimer: I am the author of the aforementioned article!

Related

Any nextjs example where I need to use _document over _app

It seems I can do everything that I need to do in both _app and _document for my application just in _app like
// _app.tsx
import Head from 'next/head';
export default function App({ Component, pageProps }) {
return (
<>
<Head>
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no"
/>
<title>Payment</title>
</Head>
<Component {...pageProps} />
</>
);
}
Then when should I use _document necessarily? I found _document pointless because:
I shouldn't place viewport meta tags in _document as nextjs compiler warnings about it. (I thought _document is a good place for markups in head because _document gets called only once on server-side and not on client-side) If I can't place everything of a kind like head markup in one place, I'd like to avoid using that way.
You'll need _document if you want to customize a page's <html> and <body> tags. For example...
Adding the global lang attribute:
<Html lang="en">
Adding custom styles:
<body className="bg-white">
_document is where you put third party links and scripts
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<link
href="https://fonts.googleapis.com/css2?family=Inter&display=optional"
rel="stylesheet"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
If you are using styled components you need to configure it in _document.jsx file.
https://github.com/massaaki/nextjs-with-styled-component/blob/main/src/pages/_document.tsx

How do I use PrimeVue in CustomElements?

I have a Vue 3 custom Element in which I want to use PrimeVue & PrimeFlex etc.
So I first create a Component, use the .ce.vue extension for the sfc mode and use the combination of defineCustomElement and customElements.define to compile it to a web component. Finally I use it in the index.html to see if it works in the Browser.
It works to some extent but not completely. For example, I am unsure about how to translate app.use(PrimeVue) for my case.
//customElement.ce.vue
<template>
<div>Test</div>
<AutoComplete field="name" />
</template>
<script lang="ts">
import { defineComponent } from "vue";
import AutoComplete from "primevue/autocomplete";
export default defineComponent({
name: "customElement",
props: {
msg: String,
},
components: { AutoComplete },
setup: () => {
console.log(JSON.stringify(theme));
return { PrimeVue };
},
styles: [],
});
</script>
<style scoped lang="scss"></style>
//main.ts
import { createApp, defineCustomElement } from "vue";
import App from "./App.vue";
//PrimeVue
import PrimeVue from "primevue/config";
import "/node_modules/primeflex/primeflex.css";
import "primevue/resources/primevue.min.css";
import "primevue/resources/themes/saga-blue/theme.css";
//CustomElement
import customElement from "#/components/customElement.ce.vue";
const customElementWC = defineCustomElement(customElement);
customElements.define("custom-element", customElementWC);
//Setup VueApplication for testing/reference, this works as expected.
const app = createApp(App);
app.use(PrimeVue);
app.mount("#app");
//index.html (for testing)
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div> //test app
<custom-element /> //the custom Web Component
<!-- built files will be auto injected -->
</body>
</html>
So I can see that the PrimeVue-Autocomplete is being shown, but the styles are not working.
So the question is:
How can I use all of PrimeVue in a custom Component?
Or in other words: How do I setup a Vue 3 CustomElement with PrimeVue?
So I have found a workaround (not a proper solution).
The way to make most of it work is to import the styles and js/ts modules in the component(s) itself.
The main styles make the most sense to import in the root component of the web component.
The reason why it has to be there is, due to:
https://github.com/vuejs/vue-cli/issues/6033 and
https://github.com/vuejs/core/issues/4662
that web components can't use plugins, or at least I don't know how. In other words there is no app.use() method.
I still couldn't properly import primeflex so I had to use the cdn link. I think it is possible to use an internal import, and I will update the answer when I find out how.
To use a specific PrimeVue component, simply import and register it as the documentation describes.
<template>
<Button />
</template>
<script lang="ts">
import Button from "primevue/button";
import { defineComponent } from "vue";
export default defineComponent({
components: { Button },
});
</script>
<style>
#import url("https://unpkg.com/primeflex#3.1.0/primeflex.css");
</style>
<style lang="scss" src="#/assets/scss/globals.scss"></style>
<style src="primevue/resources/primevue.min.css"></style>
<style src="primevue/resources/themes/tailwind-light/theme.css"></style>
//main.ts
import { defineCustomElement } from "vue";
import App from "./App.ce.vue";
customElements.define("custom-element", defineCustomElement(App));
Limitation:
Due to the missing plugin support (or my lack of knowledge of it) the lines:
import PrimeVue from 'primevue/config';
app.use(PrimeVue);
are not possible. Unfortunately, I can't fully grasp the impact that might have.

Head component from next/head renders into <body> instead of <head>

I am setting up a project in nextjs. I would like to add some global meta tags and title to the project. I used the Head component from next/head in _app.tsx but they are being rendered in the body instead of the head both on localhost and when deployed using vercel. I have used next before and can't remember running into this issue.
Here is my _app.tsx
import { AppProps } from 'next/app'
import Head from 'next/head'
import Footer from '../components/footer'
import MobileNavBar from '../components/mobileNavBar'
import NavBar from '../components/navBar'
import '../styles.scss'
const App = ({ Component, pageProps }: AppProps) => {
return (
<>
<Head>
<html lang="en" />
<title> Erika's Dog Training </title>
<meta
content="width=device-width, initial-scale=1, maximum-scale=5, shrink-to-fit=no"
name="viewport"
></meta>
<script type="text/javascript" src="https://assets.calendly.com/assets/external/widget.js"></script>
</Head>
<MobileNavBar />
<NavBar />
<Component {...pageProps} />
<Footer />
</>
)
}
export default App;
So the meta tag and title here are showing up on the page, but at the top of the <body> tag instead of in <head>
I had the same problem with next: "11.1.2". Removing <html lang="en" /> fixed the problem. I added the lang value back using: (https://melvingeorge.me/blog/set-html-lang-attribute-in-nextjs)
/* next.config.js */
module.exports = {
i18n: {
locales: ["en"],
defaultLocale: "en",
},
};
Consider the structure of this example from the nextjs documentation.
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
The main differences that I see, is that in the documentation they place both head and body as siblings within <Html> tags. I suspect that is your problem. I would also remove the empty <> and </> tags.
You have the line: <html lang="en" /> Inside the Head tag. This is going to make it challenging for next to generate the correct html as the html tag is generally above both Head and body tags.
Place <html lang="XX"/> as the last child of <Head>.
Example:
<Head>
<title> Erika's Dog Training</title>
<meta content="width=device-width, initial-scale=1, maximum-scale=5, shrink-to-fit=no" name="viewport" />
<html lang="en" />
</Head>

Set default Head for all pages in NextJS?

Can you set a default value for the Head component in NextJS that other pages will extend from?
In my case I need to load a font on every page:
<Head>
<link
href="path-to-font"
rel="stylesheet"
/>
</Head>
I could do this with a custom document file, but a default value for Head seems simpler.
https://nextjs.org/docs/advanced-features/custom-document
If the Head doesn't require change often, you can take the custom document approach. However if you want things to be dynamic like different title for different pages I would suggest keeping the static layout in custome-document and the dynamic layout separate in another component like
in _document.js
// this will define the default layout for the page
render() {
return (
<Html lang="en">
<Head>
<meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
// links for static assets
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
If you want things to be dynamic like the title, create a new Layout component that wraps all the components is a specific page.
const Layout = ({children, title="default title", description="default-description"}) => {
return (
<>
<Head>
<meta name="description" content={description} />
<title>{title}</title>
</Head>
// you can header component here
<main>
{children}
</main>
// you can add your footer component here
</>
);
}
Now you just need to wrap you page with the Layout for things to be dynamic.
const MyPage = (props) => {
return (
<Layout title="the-dynamic-title" description="the-dynamic-description">
// all other components for your page goes here
</Layout>
)
}

Is is possible import template tag with ES2015?

I'd been reading but I don't find anything if is it possible define in a different html file and import with ESModule to use with shadowRoot, could be?
index.html, where I define2 javscript modules and use my component <hello-world></hello-world>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My First Component</title>
<meta name="description" content="My First Component">
<meta name="author" content="Ismael Rodriguez">
<script type="module" src="js/my-template.js"></script>
<script type="module" src="js/component.js"></script>
<!--
<template id="my-template">
<h2>Hello World</h2>
</template>
-->
</head>
<body>
<h1>Web Component</h1>
<hello-world></hello-world>
</body>
js/my-template.js, In this module only export a string which has tags html.
export const template = `
<style>
h3 {
color: red;
font-family: helvetica;
}
</style>
<h3>Hello World</h3>
`;
js/component.js, Finally import the module my-template.js. I have found this way to interpret the template from my module using ESmodule. How Could I import the template and use in my component (with firefox support)?
import {template} from './my-template.js';
class HelloWorld extends HTMLElement{
constructor(){
super();
let shadowRoot = this.attachShadow({mode: 'open'})
const t = this.createTemplate(template);
const instance = t.content.cloneNode(true);
shadowRoot.appendChild(instance);
/*console.log(template);
const t = document.querySelector('#my-template');
const instance = t.content.cloneNode(true);
shadowRoot.appendChild(instance);*/
}
createTemplate(html){
const template = document.createElement('template');
html = html.trim();
template.innerHTML = html;
return template;
}
}
window.customElements.define('hello-world',HelloWorld);
You can only import Javascript files as ES6 Modules.
If you wan to import a element, you'll need to put it a Javascript file, for example by using a template literal string.
template.js:
export var template = `<template>
<h1>Content title</h1>
<div>Content</div>
</template>`
But it doesn't make sense. Instead you could define the content template directly.
templates.js:
export var literal1= `
<h1>Content title</h1>
<div>Content</div>
`
index.html:
<div id=host></div>
<script type="module">
import * as templates from './templates.js'
host.attachShadow( {mode:'open'} )
.innerHTML = templates.literal1
</script>
Alternatly, if you want to keep your DOM element in a HTML file, you can use fetch() to import a file, as demonstrated by the code snipped in this post about HTML Imports.

Resources