From 84cf1a9758b1e357f18efac5763d17d6a4db0578 Mon Sep 17 00:00:00 2001 From: Westbrook Johnson Date: Mon, 10 Aug 2020 14:39:35 -0400 Subject: [PATCH] feat(card): upgrade to Spectrum CSS v3.0.0 --- documentation/src/components/code-example.ts | 3 +- packages/action-menu/src/action-menu.css | 4 + packages/base/src/Base.ts | 6 +- packages/button/src/ActionButton.ts | 13 + packages/button/test/action-button.test.ts | 8 + packages/card/README.md | 203 ++++++++- packages/card/package.json | 7 +- packages/card/src/Card.ts | 223 +++++++--- packages/card/src/card.css | 16 + packages/card/src/spectrum-card.css | 408 ++++++++++++------- packages/card/src/spectrum-config.js | 97 +++-- packages/card/stories/card.stories.ts | 222 +++++++++- packages/card/test/card.test.ts | 110 ++++- packages/card/tsconfig.json | 7 +- packages/checkbox/src/checkbox.css | 4 + test/visual/stories.js | 8 + yarn.lock | 13 +- 17 files changed, 1093 insertions(+), 259 deletions(-) diff --git a/documentation/src/components/code-example.ts b/documentation/src/components/code-example.ts index 8f8671b4b4..a5fe74638c 100644 --- a/documentation/src/components/code-example.ts +++ b/documentation/src/components/code-example.ts @@ -26,6 +26,7 @@ import DarkThemeStyles from 'prismjs/themes/prism-okaidia.css'; import LightThemeStyles from 'prismjs/themes/prism.css'; import Styles from './code-example.css'; import { stripIndent } from 'common-tags'; +import { FocusVisiblePolyfillMixin } from '@spectrum-web-components/shared'; class Code extends LitElement { @property() @@ -75,7 +76,7 @@ export class LightCode extends Code { } @customElement('code-example') -export class CodeExample extends LitElement { +export class CodeExample extends FocusVisiblePolyfillMixin(LitElement) { @query('#markup') private markup?: HTMLDivElement; diff --git a/packages/action-menu/src/action-menu.css b/packages/action-menu/src/action-menu.css index 965ef773e7..f2f62a4a5b 100644 --- a/packages/action-menu/src/action-menu.css +++ b/packages/action-menu/src/action-menu.css @@ -10,6 +10,10 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ +:host([quiet]) { + min-width: 0; +} + #button { width: auto; } diff --git a/packages/base/src/Base.ts b/packages/base/src/Base.ts index f1a31aee83..6f5e8e5ce6 100644 --- a/packages/base/src/Base.ts +++ b/packages/base/src/Base.ts @@ -22,9 +22,13 @@ type Constructor> = { prototype: T; }; +export interface SpectrumInterface { + shadowRoot: ShadowRoot; +} + export function SpectrumMixin>( constructor: T -): T & Constructor { +): T & Constructor { return class SlotTextObservingElement extends constructor { public shadowRoot!: ShadowRoot; diff --git a/packages/button/src/ActionButton.ts b/packages/button/src/ActionButton.ts index 3be45d1a2a..b783b1a532 100644 --- a/packages/button/src/ActionButton.ts +++ b/packages/button/src/ActionButton.ts @@ -14,6 +14,11 @@ import { CSSResultArray, property, PropertyValues } from 'lit-element'; import { ButtonBase } from './ButtonBase.js'; import buttonStyles from './action-button.css.js'; +/** + * @element sp-card + * + * @fires change - Announces a change in the `selected` property of an action button + */ export class ActionButton extends ButtonBase { public static get styles(): CSSResultArray { return [...super.styles, buttonStyles]; @@ -41,6 +46,14 @@ export class ActionButton extends ButtonBase { return; } this.selected = !this.selected; + const applyDefault = this.dispatchEvent( + new Event('change', { + cancelable: true, + }) + ); + if (!applyDefault) { + this.selected = !this.selected; + } }; protected updated(changes: PropertyValues): void { diff --git a/packages/button/test/action-button.test.ts b/packages/button/test/action-button.test.ts index 1625a239ef..7af2fd2e03 100644 --- a/packages/button/test/action-button.test.ts +++ b/packages/button/test/action-button.test.ts @@ -48,5 +48,13 @@ describe('Button', () => { expect(el.toggles).to.be.true; expect(el.selected).to.be.true; expect(button.getAttribute('aria-pressed')).to.equal('true'); + + el.addEventListener('change', (event: Event) => event.preventDefault()); + el.click(); + await elementUpdated(el); + + expect(el.toggles).to.be.true; + expect(el.selected).to.be.true; + expect(button.getAttribute('aria-pressed')).to.equal('true'); }); }); diff --git a/packages/card/README.md b/packages/card/README.md index 83aaa736cb..6ae571122d 100644 --- a/packages/card/README.md +++ b/packages/card/README.md @@ -4,7 +4,6 @@ An `` represents a rectangular card that contains a variety of text and image layouts. Cards are typically used to encapsulate units of a data set, such as a gallery of image/caption pairs. -Spectrum has several card variations for different uses. ### Usage @@ -30,8 +29,12 @@ import { Card } from '@spectrum-web-components/card'; ## Example ```html demo - - + + Demo Image
Footer
``` @@ -41,7 +44,7 @@ import { Card } from '@spectrum-web-components/card'; By default, the title for an `sp-card` is applied via the `title` attribute, which is restricted to string content only. When HTML content is desired, a slot named `title` available for applying the title. ```html demo - +

Card title

Footer
@@ -58,12 +61,59 @@ attribute controls the main variant of the card. Normal cards can contain a title, a subtitle, a cover photo, and a footer. ```html - + + JPG
Footer
``` +### Actions + +Cards can be supplied an `actions` via a names slot. + +```html + + Demo Image +
Footer
+ + + + Deselect + + + Select Inverse + + + Feather... + + + Select and Mask... + + + + Save Selection + + + Make Work Path + + + +
+``` + +### Empty + +An empty card will still fill space in a design. + +```html + +``` + ### Quiet Quiet cards can contain a title, a subtitle, a cover photo, a description, and a footer. @@ -78,6 +128,66 @@ Quiet cards can contain a title, a subtitle, a cover photo, a description, and a ``` +When leveraging the `asset` attribute, a card can be declared as representing a `file`: + +```html +
+ + +
File Name
+
10/15/18
+
Footer
+
+
+``` + +Or a `folder`: + +```html +
+ + +
Folder Name
+
10/15/18
+
Footer
+
+
+``` + +Quiet cards will also accept `actions` via a named slot. + +```html +
+ + +
10/15/18
+ + + + Deselect + + + Select Inverse + + + Feather... + + + Select and Mask... + + + + Save Selection + + + Make Work Path + + + +
+
+``` + ### Gallery Gallery cards can contain a title, a subtitle, an image preview, a description, and a footer. @@ -85,14 +195,87 @@ Gallery cards can contain a title, a subtitle, an image preview, a description, ```html
+ +
10/15/18
+
Footer
+
+
+``` + +### Small + +The `small` attriibute can be applied to a standard card: + +```html demo +
+ -
10/15/18
Footer
``` + +A `horizontal` card: + +```html demo +
+ + + + + +
+``` + +Or a `quiet` card: + +```html demo +
+ + Demo Image +
Footer
+ + + + Deselect + + + Select Inverse + + + Feather... + + + Select and Mask... + + + + Save Selection + + + Make Work Path + + + +
+
+``` diff --git a/packages/card/package.json b/packages/card/package.json index 5bfb6949fd..ddd3e33956 100644 --- a/packages/card/package.json +++ b/packages/card/package.json @@ -47,13 +47,14 @@ "author": "", "license": "Apache-2.0", "devDependencies": { - "@spectrum-css/card": "^2.0.6" + "@spectrum-css/card": "^3.0.0-beta.2" }, "dependencies": { "@spectrum-web-components/asset": "^0.1.1", + "@spectrum-web-components/base": "^0.0.1", + "@spectrum-web-components/icons-workflow": "^0.3.2", + "@spectrum-web-components/quick-actions": "^0.0.1", "@spectrum-web-components/shared": "^0.6.0", - "lit-element": "^2.1.0", - "lit-html": "^1.0.0", "tslib": "^2.0.0" } } diff --git a/packages/card/src/Card.ts b/packages/card/src/Card.ts index 9640c526e6..ae893fefa2 100644 --- a/packages/card/src/Card.ts +++ b/packages/card/src/Card.ts @@ -12,31 +12,44 @@ governing permissions and limitations under the License. import { html, - LitElement, + SpectrumElement, property, CSSResultArray, TemplateResult, PropertyValues, -} from 'lit-element'; +} from '@spectrum-web-components/base'; import { FocusVisiblePolyfillMixin } from '@spectrum-web-components/shared/src/focus-visible.js'; import '@spectrum-web-components/asset/sp-asset.js'; +// import { MoreIcon } from '@spectrum-web-components/icons-workflow'; +import '@spectrum-web-components/checkbox/sp-checkbox.js'; +import '@spectrum-web-components/quick-actions/sp-quick-actions.js'; import cardStyles from './card.css.js'; +import { Checkbox } from '@spectrum-web-components/checkbox/src/Checkbox'; +import { ifDefined } from 'lit-html/directives/if-defined'; /** + * @element sp-card + * + * @fires change - Announces a change in the `selected` property of a card * @slot preview - This is the preview image for Gallery Cards - * @slot title - HTML content to be listed as the title * @slot cover-photo - This is the cover photo for Default and Quiet Cards + * @slot title - HTML content to be listed as the title + * @slot subtitle - HTML content to be listed as the subtitle * @slot description - A description of the card + * @slot actions - an `sp-action-menu` element outlining actions to take on the represened object * @slot footer - Footer text */ -export class Card extends FocusVisiblePolyfillMixin(LitElement) { +export class Card extends FocusVisiblePolyfillMixin(SpectrumElement) { public static get styles(): CSSResultArray { return [cardStyles]; } + @property() + public asset?: 'file' | 'folder'; + @property({ reflect: true }) - public variant: 'default' | 'gallery' | 'quiet' = 'default'; + public variant: 'standard' | 'gallery' | 'quiet' = 'standard'; @property({ type: Boolean, reflect: true }) public selected = false; @@ -44,12 +57,83 @@ export class Card extends FocusVisiblePolyfillMixin(LitElement) { @property() public title = ''; + @property({ type: Boolean, reflect: true }) + public horizontal = false; + + @property({ type: Boolean, reflect: true }) + public small = false; + + @property({ type: Boolean, reflect: true }) + public focused = false; + + @property({ type: Boolean, reflect: true }) + public toggles = false; + @property() public subtitle = ''; + public constructor() { + super(); + this.addEventListener('focusin', this.handleFocusin); + this.shadowRoot.addEventListener('focusin', this.handleFocusin); + this.addEventListener('focusout', this.handleFocusout); + } + + private handleFocusin = (event: Event): void => { + this.focused = true; + const target = event.composedPath()[0]; + if (target !== this) { + this.removeEventListener('keydown', this.handleKeydown); + return; + } + this.addEventListener('keydown', this.handleKeydown); + }; + + private handleFocusout(event: Event): void { + this.focused = false; + const target = event.composedPath()[0]; + if (target === this) { + this.removeEventListener('keydown', this.handleKeydown); + } + } + + private handleKeydown(event: KeyboardEvent): void { + const { code } = event; + /* istanbul ignore else */ + if (code === 'Space') { + this.toggleSelected(); + } + } + + private handleSelectedChange(event: Event & { target: Checkbox }): void { + const { target } = event; + this.selected = target.checked; + } + + public toggleSelected(): void { + if (!this.toggles) { + this.dispatchEvent( + new Event('click', { + bubbles: true, + composed: true, + }) + ); + return; + } + this.selected = !this.selected; + const applyDefault = this.dispatchEvent( + new Event('change', { + cancelable: true, + }) + ); + if (!applyDefault) { + this.selected = !this.selected; + } + } + protected get renderTitle(): TemplateResult { return html` -
+
${this.title} @@ -59,71 +143,102 @@ export class Card extends FocusVisiblePolyfillMixin(LitElement) { protected get renderPreviewImage(): TemplateResult { return html` - + `; } - protected renderGallery(): TemplateResult { - return html` - ${this.renderPreviewImage} -
- -
- `; - } - - protected renderQuiet(): TemplateResult { - return html` - ${this.renderPreviewImage} -
- -
-
${this.subtitle}
- -
-
- `; + private renderImage(): TemplateResult { + if (this.horizontal) { + return this.renderPreviewImage; + } + if (this.variant === 'standard') { + return html` + + + + `; + } + return this.renderPreviewImage; } - protected renderDefault(): TemplateResult { + /** + * sp-asset + * sp-quick-actions + * sp-checkbox + * + */ + protected render(): TemplateResult { return html` - - - -
-