Skip to content

Commit

Permalink
Add react icon component (#124)
Browse files Browse the repository at this point in the history
* Improve icon element DOM tree

* Add icon style

* Fix styles for  slots with icon

* Export the icon

* Add the react icon component

* Allow to register icons using the react component

* Fix tests

* Fix format

* Sanitize icon SVG strings

* Update button icon snapshots

---------

Co-authored-by: Frédéric Collonval <[email protected]>
  • Loading branch information
fcollonval and fcollonval authored Aug 5, 2024
1 parent 1583278 commit b418fb3
Show file tree
Hide file tree
Showing 68 changed files with 255 additions and 61 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ jobs:
yarn-
- name: Install dependencies
run: python -m pip install -U jupyterlab~=4.0
# Use JLab 4.0.x branch because otherwise this will not bring
# the current toolkit version but the core version one.
run: python -m pip install -U jupyterlab~=4.0.0
- name: Build the extension
run: |
set -eux
Expand Down Expand Up @@ -94,6 +96,6 @@ jobs:
sudo rm -rf $(which node)
sudo rm -rf $(which node)
pip install myextension.tar.gz
pip install jupyterlab
pip install "jupyterlab~=4.0.0"
jupyter labextension list 2>&1 | grep -ie "jupyter-ui-demo.*enabled"
python -m jupyterlab.browser_check --no-browser-test
4 changes: 2 additions & 2 deletions binder/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ channels:
dependencies:
# runtime dependencies
- python >=3.10,<3.11.0a0
- jupyterlab >=4.0.0,<5
- jupyterlab >=4.0.0,<4.1.0
# labextension build dependencies
- nodejs >=18,<19
- nodejs >=20,<21
- pip
- wheel
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ export const breadcrumbItemStyles: FoundationElementTemplate<
display: flex;
}
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
/* TODO: adaptive typography https://github.com/microsoft/fast/issues/2432 */
width: 16px;
height: 16px;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions packages/components/src/button/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { attr } from '@microsoft/fast-element';
import { Button, buttonTemplate as template } from '@microsoft/fast-foundation';
import { buttonStyles as styles } from './button.styles.js';
import { Icon } from '../icon/index.js';

/**
* Scale locally an element.
Expand Down Expand Up @@ -73,9 +74,8 @@ class JupyterButton extends Button {
);
if (
slottedElements.length === 1 &&
(slottedElements[0] instanceof SVGElement ||
slottedElements[0].classList.contains('fa') ||
slottedElements[0].classList.contains('fas'))
(slottedElements[0] instanceof Icon ||
slottedElements[0] instanceof SVGElement)
) {
this.control.classList.add('icon-only');
} else {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/components/src/custom-elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type { DesignSystemProvider } from './design-system-provider/index.js';
import type { Dialog } from './dialog/index.js';
import type { Disclosure } from './disclosure/index.js';
import type { Divider } from './divider/index.js';
import type { Icon } from './icon/index.js';
import type { Listbox } from './listbox/index.js';
import type { Menu } from './menu/index.js';
import type { MenuItem } from './menu-item/index.js';
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 11 additions & 5 deletions packages/components/src/icon/icon.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { Icon } from './index';
// Register the icon with proper SVG formatting
Icon.register({
name: 'search',
svgStr: `
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" fill="currentColor"/>
svgStr: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
fill="currentColor"
/>
</svg>`
});

Expand All @@ -25,8 +27,12 @@ const Template: StoryFn = (args, context): string => {
setTimeout(() => {
Icon.register({
name: 'search',
svgStr: `
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 576 512"><path d="M575.8 255.5c0 18-15 32.1-32 32.1l-32 0 .7 160.2c0 2.7-.2 5.4-.5 8.1l0 16.2c0 22.1-17.9 40-40 40l-16 0c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1L416 512l-24 0c-22.1 0-40-17.9-40-40l0-24 0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32l0 64 0 24c0 22.1-17.9 40-40 40l-24 0-31.9 0c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2l-16 0c-22.1 0-40-17.9-40-40l0-112c0-.9 0-1.9 .1-2.8l0-69.7-32 0c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z" fill="currentColor"/></svg>`
svgStr: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 576 512">
<path
d="M575.8 255.5c0 18-15 32.1-32 32.1l-32 0 .7 160.2c0 2.7-.2 5.4-.5 8.1l0 16.2c0 22.1-17.9 40-40 40l-16 0c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1L416 512l-24 0c-22.1 0-40-17.9-40-40l0-24 0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32l0 64 0 24c0 22.1-17.9 40-40 40l-24 0-31.9 0c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2l-16 0c-22.1 0-40-17.9-40-40l0-112c0-.9 0-1.9 .1-2.8l0-69.7-32 0c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z"
fill="currentColor"
/>
</svg>`
});
}, args.delay);
}
Expand Down
23 changes: 23 additions & 0 deletions packages/components/src/icon/icon.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

import { css, ElementStyles } from '@microsoft/fast-element';
import { display } from '@microsoft/fast-foundation';
import { heightNumber } from '../styles/index.js';

/**
* Styles for Badge
* @public
*/
export const iconStyles: ElementStyles = css`
${display('inline-block')} :host {
inline-size: ${heightNumber};
block-size: ${heightNumber};
}
:host svg {
display: block;
width: 100%;
height: 100%;
}
`;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 51 additions & 20 deletions packages/components/src/icon/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import {
FASTElement,
customElement,
attr,
html
} from '@microsoft/fast-element';
import { DOM, FASTElement, customElement, attr } from '@microsoft/fast-element';
import { iconStyles as styles } from './icon.styles';

const template = html<Icon>`<div :innerHTML="${x => x.getSvg()}"></div>`;
/**
* Icon definition
*/
export interface IconDefinition {
/**
* Icon unique name
*/
name: string;
/**
* Icon SVG as string
*/
svgStr: string;
}

/**
* Icon component
Expand All @@ -19,24 +27,24 @@ const template = html<Icon>`<div :innerHTML="${x => x.getSvg()}"></div>`;
*/
@customElement({
name: 'jp-icon',
template
styles
})
export class Icon extends FASTElement {
/**
* Name of the icon to display.
*/
@attr name: string;

private static iconsMap = new Map<string, string>();
private static _defaultIcon =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z" fill="currentColor"/></svg>';
DOM.createHTML(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16">
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z"
fill="currentColor"
/>
</svg>`);

/**
* Register a new icon.
*
* @param options { name: Icon unique name, svgStr: Icon SVG as string }
* @param options Icon definition
*/
static register(options: { name: string; svgStr: string }): void {
static register(options: IconDefinition): void {
if (Icon.iconsMap.has(options.name)) {
console.warn(
`Redefining previously loaded icon svgStr. name: ${
Expand All @@ -46,19 +54,19 @@ export class Icon extends FASTElement {
}`
);
}
Icon.iconsMap.set(options.name, options.svgStr);
Icon.iconsMap.set(options.name, DOM.createHTML(options.svgStr));

// Rerender all existing icons with the same name
document
.querySelectorAll(`jp-icon[name="${options.name}"]`)
?.querySelectorAll(`jp-icon[name="${options.name}"]`)
.forEach((node: HTMLElement) => {
node.setAttribute('name', '');
node.setAttribute('name', options.name);
});
}

/**
* Set a new default icon.
* Set the default icon.
*
* @param svgStr The SVG string to be used as the default icon.
*/
Expand All @@ -76,10 +84,33 @@ export class Icon extends FASTElement {
return Icon._defaultIcon;
}

/**
* Name of the icon to display.
*/
@attr name: string;
nameChanged() {
if (this.shadowRoot) {
this.shadowRoot.innerHTML = this.getSvg();
}
}

/**
* The connected callback for this FASTElement.
* @remarks
* This method is invoked by the platform whenever this FASTElement
* becomes connected to the document.
*/
connectedCallback(): void {
super.connectedCallback();
this.nameChanged();
}

/**
* Get the icon SVG
*/
getSvg(): string {
protected getSvg(): string {
return Icon.iconsMap.get(this.name) ?? Icon.defaultIcon();
}
}

export { styles as iconStyles };
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export * from './design-tokens.js';
export * from './dialog/index.js';
export * from './disclosure/index.js';
export * from './divider/index.js';
export * from './icon/index.js';
export * from './listbox/index.js';
export * from './menu/index.js';
export * from './menu-item/index.js';
Expand Down
17 changes: 12 additions & 5 deletions packages/components/src/menu-item/menu-item.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ export const menuItemStyles: FoundationElementTemplate<
:host([disabled]:hover) .start,
:host([disabled]:hover) .end,
:host([disabled]:hover)::slotted(svg) {
:host([disabled]:hover)::slotted(svg),
:host([disabled]:hover)::slotted(jp-icon) {
fill: ${neutralForegroundRest};
}
Expand All @@ -168,7 +169,8 @@ export const menuItemStyles: FoundationElementTemplate<
justify-content: center;
}
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
/* TODO: adaptive typography https://github.com/microsoft/fast/issues/2432 */
width: 16px;
height: 16px;
Expand All @@ -183,9 +185,11 @@ export const menuItemStyles: FoundationElementTemplate<
:host(:hover) .start,
:host(:hover) .end,
:host(:hover)::slotted(svg),
:host(:hover)::slotted(jp-icon),
:host(:active) .start,
:host(:active) .end,
:host(:active)::slotted(svg) {
:host(:active)::slotted(svg),
:host(:active)::slotted(jp-icon) {
fill: ${neutralForegroundRest};
}
Expand Down Expand Up @@ -331,9 +335,11 @@ export const menuItemStyles: FoundationElementTemplate<
:host(:hover) .start,
:host(:hover) .end,
:host(:hover)::slotted(svg),
:host(:hover)::slotted(jp-icon),
:host(:active) .start,
:host(:active) .end,
:host(:active)::slotted(svg) {
:host(:active)::slotted(svg),
:host(:active)::slotted(jp-icon) {
fill: ${SystemColors.HighlightText};
}
Expand All @@ -356,7 +362,8 @@ export const menuItemStyles: FoundationElementTemplate<
:host([disabled]:hover),
:host([disabled]:hover) .start,
:host([disabled]:hover) .end,
:host([disabled]:hover)::slotted(svg) {
:host([disabled]:hover)::slotted(svg),
:host([disabled]:hover)::slotted(jp-icon) {
background: ${SystemColors.Canvas};
color: ${SystemColors.GrayText};
fill: currentcolor;
Expand Down
6 changes: 4 additions & 2 deletions packages/components/src/option/option.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,13 @@ export const optionStyles: FoundationElementTemplate<
.start,
.end,
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
display: flex;
}
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
/* TODO: adaptive typography https://github.com/microsoft/fast/issues/2432 */
height: calc(${designUnit} * 4px);
width: calc(${designUnit} * 4px);
Expand Down
3 changes: 2 additions & 1 deletion packages/components/src/search/search.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ export const searchStyles: FoundationElementTemplate<
height: calc(100% - 2px);
}
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
/* TODO: adaptive typography https://github.com/microsoft/fast/issues/2432 */
width: 16px;
height: 16px;
Expand Down
6 changes: 4 additions & 2 deletions packages/components/src/select/select.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,8 @@ export const selectStyles: FoundationElementTemplate<
.end,
.indicator,
.select-indicator,
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
/* TODO: adaptive typography https://github.com/microsoft/fast/issues/2432 */
fill: currentcolor;
height: 1em;
Expand Down Expand Up @@ -355,7 +356,8 @@ export const selectStyles: FoundationElementTemplate<
.end,
.indicator,
.select-indicator,
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
color: ${SystemColors.ButtonText};
fill: currentcolor;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/components/src/skeleton/skeleton.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ export const skeletonStyles: FoundationElementTemplate<ElementStyles> = (
z-index: 1;
}
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
z-index: 2;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/components/src/styles/patterns/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ export const BaseButtonStyles = css`
line-height: 0;
}
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
${
/* Glyph size and margin-left is temporary -
replace when adaptive typography is figured out */ ''
Expand Down
3 changes: 2 additions & 1 deletion packages/components/src/styles/patterns/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ export const BaseFieldStyles = css`
fill: currentcolor;
}
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
/* TODO: adaptive typography https://github.com/microsoft/fast/issues/2432 */
width: 16px;
height: 16px;
Expand Down
3 changes: 2 additions & 1 deletion packages/components/src/toolbar/toolbar.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ export const toolbarStyles: FoundationElementTemplate<
margin-inline: 0;
}
::slotted(svg) {
::slotted(svg),
::slotted(jp-icon) {
/* TODO: adaptive typography https://github.com/microsoft/fast/issues/2432 */
width: 16px;
height: 16px;
Expand Down
Loading

0 comments on commit b418fb3

Please sign in to comment.