no-duplicate-selectors error for different selectors inside the same file - stylelint

I am using stylelint within a CSS-IN-JS project (here using astroturf, but I face the same pattern using any CSS-IN-JS library such as styled-components as well).
I define different styled elements within the same file, and therefore sometimes end up having duplicated selectors and/or import rules.
/* style.js */
import styled from 'astroturf';
export const StyledComponentA = styled('div')`
transform: scale(0);
&.visible {
transform: scale(1);
}
`;
export const StyledComponentB = styled('div')`
opacity: 0;
/* -> stylelint error: Unexpected duplicate selector "&.visible" */
&.visible {
opacity: 1;
}
`;
Which I compose this way:
import React from 'react';
import { StyledComponentA, StyledComponentB } from './style';
export const Component = ({ isVisible }) => (
<StyledComponentA visible={isVisible}>
<StyledComponentB visible={isVisible}>Whatever</StyledComponentB>
</StyledComponentA>
);
Is there a way to set these stylelint rules on blocks instead of an entire file?

Is there a way to set these stylelint rules on blocks instead of an entire file?
There is not.
Rules like no-duplicate-selectors are scoped to a source and stylelint treats the following as sources:
entire files
code passed to the code option of the node API
stdin passed to the CLI
When writing CSS-in-JS, it might be advisable to turn off the rules scoped to sources. You can turn them off:
entirely in your configuration object e.g. "no-duplicate-selectors": null
on a case-by-case basis using command comments

Related

how to add and select color for nodes/tree view items in explorer view in my vscode extension

I have added my own explorer view in my extension.
Here I added nodes/tree view items however I am not finding any way to customize and choose color my tree view items in explorer view.
Any idea how to achieve this?
There should be some way because when some file has error then its color is set to different compared to other open file.
[I assume this is your github issue: Not able to use FileDecorationProvider for tree view item.]
Here is my attempt at using a FileDecorationProvider for a custom TreeView. With the caveat that I am new to typescript and FileDecorations.
If you have seen Support proposed DecorationProvider api on custom views you know there are limitations on using a FileDecorationProvider for coloring TreeItem's - primarily that the decoration/coloration cannot be limited to your treeView - wherever that resourceUri apeears, like in the Explorer, your fileDecoration will be applied. That is very unfortunate but I don't believe there is any way to avoid that for now.
First, in your TreeItem class you will have to give whichever items you want decorated a resourceUri. Like this:
export class TreeTab extends vscode.TreeItem {
constructor( public readonly tab: vscode.Tab, public index: number = 0 ) {
super(tab.label, vscode.TreeItemCollapsibleState.None);
this.tab = tab;
if (tab.input instanceof vscode.TabInputText) {
this.resourceUri = tab.input.uri;
}
}
Ignore the specifics of the code for my extension, the point is:
this.resourceUri = <some vscode.Uri>;
Secondly, this is how I set up my FileDecoration class:
import {window, Tab, TabInputText, Uri, Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, ThemeColor} from 'vscode';
export class TreeFileDecorationProvider implements FileDecorationProvider {
private disposables: Array<Disposable> = [];
private readonly _onDidChangeFileDecorations: EventEmitter<Uri | Uri[]> = new EventEmitter< Uri | Uri[]>();
readonly onDidChangeFileDecorations: Event<Uri | Uri[]> = this._onDidChangeFileDecorations.event;
constructor() {
this.disposables = [];
this.disposables.push(window.registerFileDecorationProvider(this));
}
async updateActiveEditor(activeTab: Tab): Promise<void> {
if (activeTab.input instanceof TabInputText)
this._onDidChangeFileDecorations.fire(activeTab.input.uri);
// filter to get only non-activeTabs
activeTab.group.tabs.map( tab => {
if (!tab.isActive && tab.input instanceof TabInputText)
this._onDidChangeFileDecorations.fire(tab.input.uri);
});
}
async provideFileDecoration(uri: Uri): Promise<FileDecoration | undefined> {
const activeEditor = window.activeTextEditor.document.uri;
if (uri.fsPath === activeEditor.fsPath) {
return {
badge: "⇐",
color: new ThemeColor("charts.red"),
// color: new vscode.ThemeColor("tab.activeBackground"),
// tooltip: ""
};
}
else return null; // to get rid of the custom fileDecoration
}
dispose() {
this.disposables.forEach((d) => d.dispose());
}
}
provideFileDecoration(uri: Uri) does the actual decorating. It finds only certain files and decorates them, and by returning null resets that previously decorated uri (as supplied by the uri argument).
updateActiveEditor() is an exported method that I call in other parts of the extension when I want to change a file decoration. So elsewhere I have this in another file:
import { TreeFileDecorationProvider } from './fileDecorator';
export class EditorManager {
public TreeItemDecorator: TreeFileDecorationProvider;
// and then on a listener that gets triggered when I need to make a change to some things including the FileDecoration for a uri
this.TreeItemDecorator.updateActiveEditor(activeTab);
this.TreeItemDecorator.updateActiveEditor(activeTab); that calls the updateActiveEditor method in the TreeFileDecorationProvider class which calls the this._onDidChangeFileDecorations.fire(<some uri>); method for uri's that need to have the decoration applied and also for uri's that need to have the decoration removed.
this._onDidChangeFileDecorations.fire(<some uri>); will call provideFileDecoration(uri: Uri) where the actual decoration will be applied or removed depending on some state of that uri.
I am sure there is a way to call onDidChangeFileDecorations() directly from another file in your project (if you don't need to do any pre-processing of the uri like I have to do. I just haven't figured out how to construct the argument for that function yet. Perhaps someone will help on that point.
You can see here:
color: new ThemeColor("charts.red"),
// color: new vscode.ThemeColor("tab.activeBackground"),
how a color is chosen - it must be some ThemeColor. The charts theme colors has a few basic colors that are handy to refer to. See theme color references, Charts therein.
The badge option can take up to 2 characters, but as you see I copied/pasted a unicode character for mine and that works.
As I mentioned my FileDecorationProvider is called from an eventListener, but you may not need that for your use case - if decorations do not have to added and removed based on user actions like in my case. So you may be able to call your FileDecorationProvider right from your extension.ts activate() like so:
import * as vscode from 'vscode';
import { TreeFileDecorationProvider } from './fileDecorator';
export async function activate(context: vscode.ExtensionContext) {
new TreeFileDecorationProvider();
}
Other references:
a treeDecorationProvider.ts example
part of the git extension that does file decorations
Custom view decorations in VSCode extension

AngularDart structural directive add custom content

Is there some chance to simple update _viewContainer??
I am trying create directive for material-button component, that add hidden classic button for submit form by hit enter key. I need to add simple html to _viewContainer, something like this
import 'package:angular/angular.dart';
#Directive(
selector: '[kpMaterialSubmit]',
)
class KpMaterialSubmit {
final TemplateRef _templateRef;
final ViewContainerRef _viewContainer;
KpMaterialSubmit(this._viewContainer, this._templateRef);
#Input()
set kpMaterialSubmit(bool showButton) {
_viewContainer.clear();
if (showButton) {
_viewContainer.createEmbeddedView(_templateRef);
//THIS IS THE IMPORTANT PART
_viewContainer.createMyCustomView('<button class="hidden">Submit</button>');
} else {
_viewContainer.createEmbeddedView(_templateRef);
}
}
}
I have done something similar and it works. I don't know if there is a better way.
Instead of using html text directly, create a component.
Add the import to the component template:
import 'package:<your package>/<path to component>/<component source name>.template.dart';
Then add the component with the createComponent() method (instead of createMyCustomView()):
_viewContainer.createComponent(<componentName>NgFactory);
That's all!
If you need to style the component or add any attribute you can get the reference and derive the HtmlElement instance:
ComponentRef _componentRef = _viewContainer.createComponent(<componentName>NgFactory);
HtmlElement element = _componentRef.location;
element.style.background = 'green';
element.setAttribute('value', 'text');

Best practice for testing for data-testid in a nested component with React Testing Library?

I'm trying to write a test to check if my app is rendering correctly. On the initial page Ive added a data-testid of "start". So my top level test checks that the initial component has been rendered.
import React from "react";
import { render } from "react-testing-library";
import App from "../App";
test("App - Check the choose form is rendered", () => {
const wrapper = render(<App />);
const start = wrapper.getByTestId("start");
// console.log(start)
// start.debug();
});
If I console.log(start) the I can see all the properties of the node. However if I try and debug() then it errors saying it's not a function.
My test above does seem to work. If I change the getByTestId from start to anything else then it does error. But I'm not using the expect function so am I violating best practices?
There are two parts to this question -
Why console.log(start) works and why not start.debug()?
getByTestId returns an HTMLElement. When you use console.log(start), the HTMLElement details are logged. But an HTMLElement does not have debug function. Instead, react-testing-library provides you with a debug function when you use render to render a component. So instead of using start.debug(), you should use wrapper.debug().
Because you don't have an expect function, is it a good practice to write such tests ?
I am not sure about what could be a great answer to this, but I will tell the way I use it. There are two variants for getting an element using data-testid - getByTestId and queryByTestId. The difference is that getByTestId throws error if an element with the test id is not found whereas queryByTestId returns null in such case. This means that getByTestId in itself is an assertion for presence of element. So having another expect which checks if the element was found or not will be redundant in case you are using getByTestId. I would rather use queryByTestId if I am to assert the presence/absence of an element. Example below -
test("App - Check the "Submit" button is rendered", () => {
const { queryByTestId } = render(<App />)
expect(queryByTestId('submit')).toBeTruthy()
});
I would use getByTestId in such tests where I know that the element is present and we have expects for the element's properties (not on the element's presence/absence). Example below -
test("App - Check the "Submit" button is disabled by default", () => {
const { getByTestId } = render(<App />)
expect(getByTestId('submit')).toHaveClass('disabled')
});
In the above test, if getByTestId is not able to find the submit button, it fails by throwing an error, and does not execute the toHaveClass. Here we don't need to test for presence/absence of the element, as this test is concerned only with the "disabled" state of the button.

Material-UI: "The key provided to the classes property is not implemented"

I am using the withStyles() HOC to override some MUI component styles, theme and breakpoints.
There is obviously something I do not understand here as I keep getting errors such as this one:
Warning: Material-UI: the key tab provided to the classes property
is not implemented in Items.
You can only override one of the following:
card,details,content,cover,avatar,lock
Example code: https://codesandbox.io/s/6xwz50kxn3
I have a <List /> component and its child <Items />.
My intention is to apply the styles in the demo.js file only to the <List /> component, and the styles in the demoChild.js to the <Items /> Component.
I would really appreciate an explanation of what I'm doing wrong, and maybe a solution?
Note: I have found other posts with the same error, but they seem to have something different to my example.
The warnings are caused by this line in your demo.js file:
<Items {...this.props} items={items} />
You're spreading all of List's props down into your Items. One of these props is classes, containing all of the CSS classes you define in demo.js. Since those are intended for List, they include CSS classes that are implemented by List but not Items. Since Items is receiving this prop, it's reading it as you trying to override classes that aren't available and warning you about it.
You can fix this problem by spreading only the unused props:
// Use other to capture only the props you're not using in List
const { classes, headerIsHidden, ...other } = this.props;
// Then spread only those unused props
<Items {...other} items={items} /
Then, you won't be spreading classes object into Items, so you won't get any warnings about classes that aren't implemented.
In my case, I want to reuse multiple styles in different files, so I wrote a helper function:
import { withStyles } from '#material-ui/core/styles'
// Fixed: material-ui "The key provided to the classes property is not implemented"
const withMultipleStyles = (...params) => {
return withStyles((theme) => {
var styles = {}
for (var len = params.length, key = 0; key < len; key++) {
styles = Object.assign(styles, params[key](theme));
}
return styles
})
}
export default withMultipleStyles
Usage:
import React from 'react'
import compose from 'recompose/compose'
import { connect } from 'react-redux'
import { style1, style2, withMultipleStyles } from '../../styles'
class your_class_name_here extends React.Component {
// your implementation
}
export default compose(
withMultipleStyles(style1, style2),
withWidth(),
connect(mapStateToProps, mapDispatchToProps)
)(your_class_name_here)

Is it fine to mutate attributes of React-controlled DOM elements directly?

I'd like to use headroom.js with React. Headroom.js docs say:
At it's most basic headroom.js simply adds and removes CSS classes from an element in response to a scroll event.
Would it be fine to use it directly with elements controlled by React? I know that React fails badly when the DOM structure is mutated, but modifying just attributes should be fine. Is this really so? Could you show me some place in official documentation saying that it's recommended or not?
Side note: I know about react-headroom, but I'd like to use the original headroom.js instead.
EDIT: I just tried it, and it seems to work. I still don't know if it will be a good idea on the long run.
If React tries to reconcile any of the attributes you change, things will break. Here's an example:
class Application extends React.Component {
constructor() {
super();
this.state = {
classes: ["blue", "bold"]
}
}
componentDidMount() {
setTimeout(() => {
console.log("modifying state");
this.setState({
classes: this.state.classes.concat(["big"])
});
}, 2000)
}
render() {
return (
<div id="test" className={this.state.classes.join(" ")}>Hello!</div>
)
}
}
ReactDOM.render(<Application />, document.getElementById("app"), () => {
setTimeout(() => {
console.log("Adding a class manually");
const el = document.getElementById("test");
if (el.classList)
el.classList.add("grayBg");
else
el.className += ' grayBg';
}, 1000)
});
And here's the demo: https://jsbin.com/fadubo/edit?js,output
We start off with a component that has the classes blue and bold based on its state. After a second, we add the grayBg class without using React. After another second, the component sets its state so that the component has the classes blue, bold, and big, and the grayBg class is lost.
Since the DOM reconciliation strategy is a black box, it's difficult to say, "Okay, my use case will work as long as React doesn't define any classes." For example, React might decide it's better to use innerHTML to apply a large list of changes rather than setting attributes individually.
In general, if you need to do manual DOM manipulation of a React component, the best strategy is to wrap the manual operation or plugin in its own component that it can 100% control. See this post on Wrapping DOM Libs for one such example.