How to focus a styled component? - styled-components

I have a styled component:
const StyledComponent = styled.div`
...
`;
and I want to focus it when the component that uses it is mounted:
class someComponent extends React.Component {
componentDidMount() {
this.sc.focus();
}
render() {
return (
<StyledComponent innerRef={(elem) => { this.sc = elem; }}>
...
</StyledComponent>
);
}
}
this technique does not work - is there a solution for this?

You can't focus non-interactive elements without the use of tabIndex ("tabindex" in normal HTML). The "div" element is considered non-interactive (unlike anchor links "a" and button controls "button".)
If you want to be able to focus this element, you'll need to add a tabIndex prop. You can use .attrs if desired to have it be added automatically so you don't need to write the prop every time.

This link
has an example with both React 16 React.createRef() and the callback method.
Have you tried setting the autoFocus attribute, or is that not fit for your use case?

Try passing the autoFocus prop to your StyledComponent like <StyledComponent autoFocus />.

Related

How to change styles of react component using styled components

I have this React component which is a simple button component:
const Button = ({ children }) => <button>{children}</button>;
I tried to pass the above component inside a styled in order to try to change its styles like this:
const StyledButton = styled(Button)`
color: yellow; //does not work
button {
color: yellowgreen; //does not work
}
`;
I am new to styled components so I am not even sure this is possible to do.
Thank you in advance for the help!
When using styled-components for your custom React components, styled-components needs to know where to inject the CSS you want to give to your <button> tag. This is done by passing the className prop to your Button component and passing it as a prop to the <button> tag.
Please try to edit your code like so:
const Button = ({ children, className }) => <button className={className}>{children}</button>;
You can read more about it here Styled Components - Existing CSS.

Drawer Component Backdrop blocking users from interacting with page will it is open

I have a Drawer Component anchored at the bottom but I would still like to interact with the page above the drawer but either I can click out of if but the drawer closes so I tried the variants persistent and permanent both didn't work they actually made it so nothing at all happens when I click out of if. I think it has something to do with the spacing or padding above, but if anyone knows how to disable that, it would be greatly appreciated.
I solved it slightly differently, by removing the "inset" CSS property of the .MuiDrawer-modal div:
.MuiDrawer-modal {
inset: unset !important;
}
Figured out my problem, I ended us having to do some height changes to the Paper component and it seemed to work the way I wanted. You can overrided the css with makeStyles method in the #material-ui/core/styles directory. I used the classes property example
// Outside the component
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
drawer: {
css here ...
}
})
// Inside Component
const classes = useStyles() // the make styles returns a function and calling the useStyles returns an object with the css.
// Inside return
<Drawer
anchor="bottom"
classes={{ paper: classes.drawer }}
>
Content...
</Drawer>

LitElement <slot> not wokring

I'm creating my custom accordion element. In which I'll have 2 components 1 for ul and other for li.
Content in file accordion-ul.ts, in which I've a slot where I want my li.
import { html, customElement, property, LitElement } from 'lit-element';
import { Accordion } from 'carbon-components';
#customElement('accordion-panel')
export default class AccordionPanel extends LitElement {
firstUpdated() {
const accordionElement = document.getElementById('accordion');
Accordion.create(accordionElement);
}
connectedCallback() {
super.connectedCallback();
}
render() {
return html`
<ul data-accordion class="accordion" id="accordion">
<slot></slot>
</ul>
`;
}
createRenderRoot() {
return this;
}
}
NOTE: I'm getting an error in the console in the firstUpdated() : Uncaught (in promise) TypeError: Cannot read property 'nodeName' of null.
The way I'm using it for testing:
<accordion-panel><li>test</li></accordion-panel>
IDK, it's not working and nothing is printing on the screen. On inspecting the element, I can see there's empty in DOM.
Your problem is that you're trying to use slots, which are a shadow DOM feature but you're not using shadow DOM (since you're overwriting the createRenderRoot method to prevent the creation of the shadowRoot)
So, if you want to use slots, just remove the createRenderRoot function from your class and use shadow DOM
Edit:
You should also update your firstUpdated method so that this part:
const accordionElement = document.getElementById('accordion');
Uses the element from your shadow DOM
const accordionElement = this.shadowRoot.querySelector('.accordion');
Then again, CarbonComponents styling will probably not work so you'll need to add those in some other way

React-like refs in lit-html / lit-element?

Does lit-html have by any change something like React's ref feature?
For example in the following pseudo-code inputRef would be a callback function or an object { current: ... } where lit-html could pass/set the HTMLElement instance of the input element when the input element is created/attached.
// that #ref pseudo-attribute is fictional
html`<div><input #ref={inputRef}/></div>`
Thanks.
In lit-element you can use #query property decorator. It's just syntactic sugar around this.renderRoot.querySelector().
import { LitElement, html, query } from 'lit-element';
class MyElement extends LitElement {
#query('#first')
first;
render() {
return html`
<div id="first"></div>
<div id="second"></div>
`;
}
}
lit-html renders directly to the dom so you don't really need refs like you do in react, you can use querySelector to get a reference to the rendered input
Here's some sample code if you were only using lit-html
<html>
<head>
<title>lit-html example</title>
<script type="module">
import { render, html } from 'https://cdn.pika.dev/lit-html/^1.1.2';
const app = document.querySelector('.app');
const inputTemplate = label => {
return html `<label>${label}<input value="rendered input content"></label>`;
};
// rendering the template
render(inputTemplate('Some Label'), app);
// after the render we can access it normally
console.log(app.querySelector('input').value);
</script>
</head>
<body>
<div class="app"></div>
<label>
Other random input
<input value="this is not the value">
</label>
</body>
</html>
If you're using LitElement you could access to the inner elements using this.shadowRoot.querySelector if you're using shadow dom or this.querySelector if you aren't
As #WeiChing has mentioned somewhere above, since Lit version 2.0 you can use the newly added directive ref for that:
https://lit.dev/docs/templates/directives/#ref
-- [EDIT - 6th October 2021] ----------------------------
Since lit 2.0.0 has been released my answer below
is completely obsolete and unnecessary!
Please check https://lit.dev/docs/api/directives/#ref
for the right solution.
---------------------------------------------------------
Even if this is not exactly what I have asked for:
Depending on the concrete use case, one option to consider is the use of directives.
In my very special use-case it was for example (with a little luck and a some tricks) possible to simulate more or less that ref object behavior.
const inputRef = useElementRef() // { current: null, bind: (special-lit-html-directive) }
...
return html`<div><input ref=${inputRef.bind}/></div>`
In my use case I could do the following:
Before rendering, set elementRef.current to null
Make sure that elementRef.current cannot be read while the component is rerendered (elementRef.current is not needed while rendering and an exception will be thrown if someone tries to read it in render phase)
That elementRef.bind directive will fill elementRef.current with the actual DOM element if available.
After that, elementRef.current can be read again.
For lit-html v1, you can define your own custom Derivative:
import { html, render, directive } from "lit-html";
function createRef(){ return {value: null}; }
const ref = directive((refObj) => (attributePart) => {
refObj.value = attributePart.committer.element;
});
const inputRef = createRef();
render(html`<input ref=${ref(inputRef)} />`;
// inputRef.value is a reference to rendered HTMLInputElement

Why is theme undefined in styled component props?

Here I access theme by passing a callback function to the styled tag. I guess styled calls this callback function with the props as first argument. This works well.
export default function SectionHeading(props: SectionHeadingProps) {
const Heading = styled.h2`
${props => props.theme.green && `
color: green;
`}
`;
return (
<Heading>{propss.children}</Heading>
);
}
In this example I pass an expression that contains the props the component has received. Here, theme is undefined.
export default function SectionHeading(props: SectionHeadingProps) {
const Heading = styled.h2`
${props.theme.green && `
color: green;
`}
`;
return (
<Heading>{props.children}</Heading>
);
}
Why is theme undefined in the second example?
The reason is that these are different "props" and they are evaluated in different times, in the first example, the props are the props passed to the styled component, augmented with theme (provided you used <ThemeProvider .../>. In the second example, it is the props passed to your component.
The injection of the theme is done by styled-component library and only to styled components. Your component doesn't get it (because it is not a styled component).
Btw, your code has redundant nesting and creates a styled component each time it is invoked.
The way to do it is to simply define:
const SectionHeading = styled.div`
${props => (props.theme && props.theme.green && {color: 'green'})};
`;
and then:
export default SectionHeading;
Note that your sample code has a typo in the first part, you wrote {propss.children} (an extra 's').

Resources