diff --git a/.github/workflows/production-build.yml b/.github/workflows/production-build.yml index ab25b7f67..af73f4037 100644 --- a/.github/workflows/production-build.yml +++ b/.github/workflows/production-build.yml @@ -25,7 +25,6 @@ jobs: app-configuration: production nx-command: run-many build-compodoc: false - build-storybook: false - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 @@ -37,6 +36,7 @@ jobs: - name: Deploy to S3 run: | aws s3 sync --delete deploy/apps/ s3://${{ secrets.AWS_S3_BUCKET_NAME }}/ui/ + aws s3 sync deploy/storybook/ s3://${{ secrets.AWS_S3_BUCKET_NAME }}/ui/storybook/ aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_DISTRIBUTION_ID }} --paths "/ui/*" aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_HUMANATLAS_IO_DISTRIBUTION_ID }} --paths "/*" diff --git a/.github/workflows/staging-build.yml b/.github/workflows/staging-build.yml index 1a75ee0a8..97f5ce905 100644 --- a/.github/workflows/staging-build.yml +++ b/.github/workflows/staging-build.yml @@ -25,7 +25,6 @@ jobs: app-configuration: staging nx-command: run-many build-compodoc: false - build-storybook: false - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 @@ -37,6 +36,7 @@ jobs: - name: Deploy to S3 run: | aws s3 sync --delete deploy/apps/ s3://${{ secrets.AWS_S3_BUCKET_NAME }}/ui--staging/ + aws s3 sync deploy/storybook/ s3://${{ secrets.AWS_S3_BUCKET_NAME }}/ui--staging/storybook/ aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_DISTRIBUTION_ID }} --paths "/ui--staging/*" - name: Publish diff --git a/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.spec.ts b/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.spec.ts index ddf12f989..258011ec3 100644 --- a/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.spec.ts +++ b/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.spec.ts @@ -5,7 +5,7 @@ import { mockDeep } from 'jest-mock-extended'; import embed, { Result } from 'vega-embed'; import { provideScrolling } from '@hra-ui/design-system/scrolling'; -import { rgbToHex } from '../models/color'; +import { rgbToHex } from '@hra-ui/design-system/color-picker'; import { ColorMapEntry, DEFAULT_COLOR_MAP_KEY, DEFAULT_COLOR_MAP_VALUE_KEY } from '../models/color-map'; import { EdgeEntry } from '../models/edge'; import { DEFAULT_NODE_TARGET_KEY, DEFAULT_NODE_TARGET_VALUE, NodeEntry } from '../models/node'; diff --git a/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.ts b/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.ts index 02844be16..02cf471b1 100644 --- a/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.ts +++ b/libs/cde-visualization/src/lib/cde-visualization/cde-visualization.component.ts @@ -18,7 +18,7 @@ import { MetadataComponent } from '../components/metadata/metadata.component'; import { NodeDistVisualizationComponent } from '../components/node-dist-visualization/node-dist-visualization.component'; import { VisualizationHeaderComponent } from '../components/visualization-header/visualization-header.component'; import { CellTypeEntry } from '../models/cell-type'; -import { rgbToHex } from '../models/color'; +import { rgbToHex } from '@hra-ui/design-system/color-picker'; import { ColorMapColorKey, ColorMapEntry, diff --git a/libs/cde-visualization/src/lib/components/cell-types/cell-types.component.ts b/libs/cde-visualization/src/lib/components/cell-types/cell-types.component.ts index f3334c0d3..8ab3d9a51 100644 --- a/libs/cde-visualization/src/lib/components/cell-types/cell-types.component.ts +++ b/libs/cde-visualization/src/lib/components/cell-types/cell-types.component.ts @@ -24,7 +24,7 @@ import { ScrollingModule } from '@hra-ui/design-system/scrolling'; import { ColorPickerModule } from 'ngx-color-picker'; import { map } from 'rxjs'; import { CellTypeEntry } from '../../models/cell-type'; -import { Rgb } from '../../models/color'; +import { Rgb } from '@hra-ui/design-system/color-picker'; import { TOOLTIP_POSITION_RIGHT_SIDE } from '../../shared/tooltip-position'; import { ColorPickerLabelComponent } from '../color-picker-label/color-picker-label.component'; diff --git a/libs/cde-visualization/src/lib/components/color-picker-label/color-picker-label.component.ts b/libs/cde-visualization/src/lib/components/color-picker-label/color-picker-label.component.ts index c8c0f85b7..05796aef6 100644 --- a/libs/cde-visualization/src/lib/components/color-picker-label/color-picker-label.component.ts +++ b/libs/cde-visualization/src/lib/components/color-picker-label/color-picker-label.component.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, effect, input, model, output, signal } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { ColorPickerDirective, ColorPickerModule } from 'ngx-color-picker'; -import { Rgb, colorEquals, hexToRgb, rgbToHex } from '../../models/color'; +import { Rgb, colorEquals, hexToRgb, rgbToHex } from '@hra-ui/design-system/color-picker'; import { TOOLTIP_POSITION_COLOR_PICKER_LABEL } from '../../shared/tooltip-position'; /** Maximum cell width for the cell type label */ diff --git a/libs/cde-visualization/src/lib/components/histogram/histogram.component.spec.ts b/libs/cde-visualization/src/lib/components/histogram/histogram.component.spec.ts index 5174e2105..132e23609 100644 --- a/libs/cde-visualization/src/lib/components/histogram/histogram.component.spec.ts +++ b/libs/cde-visualization/src/lib/components/histogram/histogram.component.spec.ts @@ -6,7 +6,7 @@ import userEvent from '@testing-library/user-event'; import { mockClear, mockDeep } from 'jest-mock-extended'; import embed, { Result } from 'vega-embed'; import { CellTypeEntry } from '../../models/cell-type'; -import { Rgb } from '../../models/color'; +import { Rgb } from '@hra-ui/design-system/color-picker'; import { EdgeEntry } from '../../models/edge'; import { DEFAULT_NODE_TARGET_KEY, NodeEntry } from '../../models/node'; import { FileSaverService } from '../../services/file-saver/file-saver.service'; diff --git a/libs/cde-visualization/src/lib/components/histogram/histogram.component.ts b/libs/cde-visualization/src/lib/components/histogram/histogram.component.ts index 1310091bf..96b9e8349 100644 --- a/libs/cde-visualization/src/lib/components/histogram/histogram.component.ts +++ b/libs/cde-visualization/src/lib/components/histogram/histogram.component.ts @@ -25,7 +25,7 @@ import { ColorPickerDirective, ColorPickerModule } from 'ngx-color-picker'; import { View } from 'vega'; import embed, { VisualizationSpec } from 'vega-embed'; import { CellTypeEntry } from '../../models/cell-type'; -import { Rgb, colorEquals, rgbToHex } from '../../models/color'; +import { Rgb, colorEquals, rgbToHex } from '@hra-ui/design-system/color-picker'; import { EdgeEntry, EdgeIndex, edgeDistance } from '../../models/edge'; import { NodeEntry, NodeTargetKey } from '../../models/node'; import { FileSaverService } from '../../services/file-saver/file-saver.service'; diff --git a/libs/cde-visualization/src/lib/models/cell-type.ts b/libs/cde-visualization/src/lib/models/cell-type.ts index 75b290530..944d9cc2b 100644 --- a/libs/cde-visualization/src/lib/models/cell-type.ts +++ b/libs/cde-visualization/src/lib/models/cell-type.ts @@ -1,4 +1,4 @@ -import { Rgb } from './color'; +import { Rgb } from '@hra-ui/design-system/color-picker'; /** Interface representing a cell type entry */ export interface CellTypeEntry { diff --git a/libs/cde-visualization/src/lib/models/color-map.ts b/libs/cde-visualization/src/lib/models/color-map.ts index 5fa7b989d..3010d1e8b 100644 --- a/libs/cde-visualization/src/lib/models/color-map.ts +++ b/libs/cde-visualization/src/lib/models/color-map.ts @@ -1,5 +1,5 @@ import { Brand } from './brand'; -import { Rgb } from './color'; +import { Rgb } from '@hra-ui/design-system/color-picker'; /** Type representing a key for color map types, enhanced with a branding mechanism */ export type ColorMapTypeKey = string & Brand<'ColorMapTypeKey'>; diff --git a/libs/cde-visualization/src/lib/shared/color-generator.ts b/libs/cde-visualization/src/lib/shared/color-generator.ts index 55a70ec79..ea8d6e84a 100644 --- a/libs/cde-visualization/src/lib/shared/color-generator.ts +++ b/libs/cde-visualization/src/lib/shared/color-generator.ts @@ -1,4 +1,4 @@ -import { Rgb, hexToRgb } from '../models/color'; +import { Rgb, hexToRgb } from '@hra-ui/design-system/color-picker'; /** Array of predefined colors in RGB format */ const COLORS: Rgb[] = [ diff --git a/libs/design-system/button/src/index.ts b/libs/design-system/button/src/index.ts index f490402b8..72641517b 100644 --- a/libs/design-system/button/src/index.ts +++ b/libs/design-system/button/src/index.ts @@ -2,7 +2,6 @@ export * from './lib/providers'; export * from './lib/button.module'; export * from './lib/button-size/button-size.directive'; export * from './lib/directives/call-to-action-button.directive'; -export * from './lib/directives/nav-item-button.directive'; export * from './lib/directives/navigation-category-button.directive'; export * from './lib/directives/primary-button.directive'; export * from './lib/directives/secondary-button.directive'; diff --git a/libs/design-system/button/src/lib/button-size/button-size.directive.ts b/libs/design-system/button/src/lib/button-size/button-size.directive.ts index 4f5363d60..439beaf40 100644 --- a/libs/design-system/button/src/lib/button-size/button-size.directive.ts +++ b/libs/design-system/button/src/lib/button-size/button-size.directive.ts @@ -1,37 +1,8 @@ -import { computed, Directive, input } from '@angular/core'; +import { Directive, input } from '@angular/core'; /** Input options for icon button size */ export type ButtonSize = 'small' | 'medium' | 'large'; -/** Interface for button size and font variable */ -interface ButtonConfig { - /** Size of the button */ - size: number; - /** Font variable for the button */ - font: string; - /** Padding for the current button size */ - horizontalPadding: number; -} - -/** Record of button sizes (number in rem) */ -const BUTTON_CONFIG: Record = { - small: { - size: 1.75, - font: '--sys-label-small', - horizontalPadding: 0.5, - }, - medium: { - size: 2, - font: '--sys-label-medium', - horizontalPadding: 0.75, - }, - large: { - size: 2.5, - font: '--sys-label-large', - horizontalPadding: 1, - }, -}; - /** * Directive for button sizes */ @@ -39,24 +10,10 @@ const BUTTON_CONFIG: Record = { selector: '[hraButtonSize]', standalone: true, host: { - '[style.--mdc-text-button-container-height.rem]': 'buttonSize()', - '[style.font]': 'fontVar()', - '[style.--mat-text-button-horizontal-padding.rem]': 'padding()', - '[style.--mdc-filled-button-container-height.rem]': 'buttonSize()', - '[style.--mat-filled-button-horizontal-padding.rem]': 'padding()', - '[style.--mat-standard-button-toggle-height.rem]': 'buttonSize()', + '[class]': '"button-size-"+size()', }, }) export class ButtonSizeDirective { /** Size of icon button to use */ readonly size = input.required({ alias: 'hraButtonSize' }); - - /** Gets size of button in rem */ - protected readonly buttonSize = computed(() => BUTTON_CONFIG[this.size()].size); - - /** Gets the font variable for the current button size */ - protected readonly fontVar = computed(() => `var(${BUTTON_CONFIG[this.size()].font})`); - - /** Gets the horizontal padding for the current button size */ - protected readonly padding = computed(() => BUTTON_CONFIG[this.size()].horizontalPadding); } diff --git a/libs/design-system/button/src/lib/button-styles/button-styles.component.scss b/libs/design-system/button/src/lib/button-styles/button-styles.component.scss index 0443e4b94..9a881d061 100644 --- a/libs/design-system/button/src/lib/button-styles/button-styles.component.scss +++ b/libs/design-system/button/src/lib/button-styles/button-styles.component.scss @@ -17,64 +17,16 @@ $y-multiplier: 0.4566; white-space: nowrap; direction: ltr; } -// Basic Primary & Secondary -:is(a, button)[mat-button] { - min-width: unset; - white-space: nowrap; - font: var(--sys-title-small); - --mdc-text-button-label-text-color: var(--sys-on-tertiary-fixed); - --mat-text-button-state-layer-color: var(--sys-on-tertiary-fixed); - --mat-text-button-hover-state-layer-opacity: 0.04; - --mat-text-button-pressed-state-layer-opacity: 0.08; - --mat-text-button-focus-state-layer-opacity: 0; - --mdc-text-button-container-shape: 0.25rem; - --mat-text-button-horizontal-padding: 1rem; - padding: 0.25rem var(--mat-text-button-horizontal-padding); - &:focus-visible { - outline: solid 2px var(--sys-tertiary); - --mdc-text-button-label-text-color: var(--sys-secondary); - } - - &.secondary-button { - --mat-text-button-state-layer-color: var(--sys-secondary); - --mdc-text-button-label-text-color: var(--sys-secondary); - } -} - -// Flat Primary & Secondary -:is(a, button)[mat-flat-button] { - font: var(--sys-title-small); - --mdc-filled-button-container-shape: 0.25rem; - --mdc-filled-button-container-color: var(--sys-tertiary); - --mat-filled-button-horizontal-padding: 1rem; +* { --mat-filled-button-pressed-state-layer-opacity: 0.16; - padding: 0.5rem var(--mat-filled-button-horizontal-padding); - - &:focus-visible { - --mdc-filled-button-container-color: var(--sys-on-primary); - --mdc-filled-button-label-text-color: var(--sys-secondary); - outline: solid 2px var(--sys-tertiary); - } - - &.secondary-button { - --mdc-filled-button-label-text-color: var(--sys-on-tertiary-fixed); - --mdc-filled-button-container-color: var(--sys-on-primary); - --mat-filled-button-hover-state-layer-opacity: 0.08; - --mat-filled-button-pressed-state-layer-opacity: 0.16; - --mat-filled-button-state-layer-color: var(--sys-on-tertiary-fixed); - outline: solid 1px var(--sys-on-tertiary-fixed); - - &:focus-visible { - --mat-filled-button-state-layer-color: var(--sys-on-primary); - --mdc-filled-button-label-text-color: var(--sys-secondary); - outline: solid 2px var(--sys-tertiary); - } - } + --mdc-filled-button-container-color: var(--sys-tertiary); + --mdc-filled-button-disabled-label-text-color: color-mix(in srgb, var(--sys-on-surface) 40%, transparent); } // CTA Primary :is(a, button)[mat-flat-button]:not(.secondary-button).cta-button { + --mdc-filled-button-container-color: var(--sys-tertiary); --mdc-filled-button-container-shape: 0; clip-path: polygon( /* Bottom left */ 0 100%, @@ -84,8 +36,31 @@ $y-multiplier: 0.4566; /* Bottom right x-axis */ calc(100% - $corner-width) 100% ); + &:hover { + background-color: transparent; + clip-path: unset; + box-shadow: unset; + filter: drop-shadow(0px 0px 16px color-mix(in srgb, var(--sys-tertiary) 56%, transparent)); + + &:before { + content: ''; + position: absolute; + z-index: -1; + inset: 0; + background: var(--mdc-filled-button-container-color); + clip-path: polygon( + /* Bottom left */ 0 100%, + /* Top left */ 0 0, + /* Top right */ 100% 0%, + /* Bottom right y-axis */ 100% calc(100% - $corner-height), + /* Bottom right x-axis */ calc(100% - $corner-width) 100% + ); + } + } + &:focus-visible { - --mdc-filled-button-container-color: var(--sys-tertiary); + --mdc-filled-button-label-text-color: var(--sys-secondary); + outline: unset; } &:focus-visible::after { @@ -106,36 +81,154 @@ $y-multiplier: 0.4566; // CTA Secondary :is(a, button)[mat-flat-button].cta-button.secondary-button { - clip-path: unset; - outline: unset; + --mdc-filled-button-container-color: var(--sys-on-primary); + --mdc-filled-button-label-text-color: var(--sys-on-tertiary-fixed); + --mat-filled-button-hover-state-layer-opacity: 0.08; + --mat-filled-button-pressed-state-layer-opacity: 0.16; + --mat-filled-button-state-layer-color: var(--sys-on-tertiary-fixed); --mdc-filled-button-container-shape: 0; + --mdc-filled-button-disabled-container-color: var(--sys-on-primary); + + &:hover { + box-shadow: none; + } &:focus-visible { + --mat-filled-button-state-layer-color: var(--sys-on-primary); + --mdc-filled-button-label-text-color: var(--sys-secondary); outline: solid 2px var(--sys-tertiary); } } +// This css clips the focus, hover, ripple states of the button for the Primary CTA +.mat-mdc-unelevated-button.cta-button:not(.secondary-button) { + .mat-mdc-button-persistent-ripple, + .mat-mdc-button-ripple, + &:hover .mat-mdc-button-persistent-ripple::before { + clip-path: polygon( + /* Bottom left */ 0 100%, + /* Top left */ 0 0, + /* Top right */ 100% 0%, + /* Bottom right y-axis */ 100% calc(100% - $corner-height), + /* Bottom right x-axis */ calc(100% - $corner-width) 100% + ); + } +} + +// Flat Round +:is(a, button)[mat-flat-button] { + font: var(--sys-label-medium); + letter-spacing: var(--sys-label-medium-tracking); + --mdc-filled-button-container-shape: 0.25rem; + --mat-filled-button-horizontal-padding: 1rem; + --mdc-filled-button-container-height: 2rem; + + &:hover { + box-shadow: 1px 2px 16px 0px rgb(from var(--sys-tertiary) r g b / 0.56); + } + + &:focus-visible { + --mdc-filled-button-container-color: var(--sys-on-primary); + --mdc-filled-button-label-text-color: var(--sys-secondary); + outline: solid 2px var(--sys-tertiary); + } +} + +// Custom class for Flat primary & Seconday +:is(a, button)[mat-flat-button] { + &.button-size-large { + --mat-filled-button-horizontal-padding: 1rem; + --mdc-filled-button-container-height: 2.5rem; + font: var(--sys-label-medium); + letter-spacing: var(--sys-label-medium-tracking); + padding: 0.5rem var(--mat-filled-button-horizontal-padding); + } +} + +// Basic Primary & Secondary +:is(a, button)[mat-button] { + min-width: unset; + font: var(--sys-label-medium); + letter-spacing: var(--sys-label-medium-tracking); + --mdc-text-button-label-text-color: var(--sys-on-tertiary-fixed); + --mat-text-button-state-layer-color: var(--sys-on-tertiary-fixed); + --mdc-text-button-disabled-label-text-color: color-mix(in srgb, var(--sys-on-surface) 40%, transparent); + --mat-text-button-pressed-state-layer-opacity: 0.16; + --mat-text-button-focus-state-layer-opacity: 0; + --mdc-text-button-container-shape: 0.25rem; + --mat-text-button-horizontal-padding: 1rem; + + &:focus-visible { + outline: solid 2px var(--sys-tertiary); + --mdc-text-button-label-text-color: var(--sys-secondary); + } + + &.secondary-button { + --mat-text-button-state-layer-color: var(--sys-secondary); + --mdc-text-button-label-text-color: var(--sys-secondary); + } + + &:has(mat-icon).mat-mdc-button { + padding: 0 1rem 0 0.75rem; + mat-icon { + margin-right: 0.5rem; + } + } +} + +// Custom Class for basic primary and secondary +:is(a, button)[mat-button] { + &.button-size-medium { + --mat-text-button-horizontal-padding: 1rem; + --mdc-text-button-container-height: 2.5rem; + font: var(--sys-label-medium); + letter-spacing: var(--sys-label-medium-tracking); + padding: 0.5rem var(--mat-text-button-horizontal-padding); + } + + &.button-size-small { + --mat-text-button-horizontal-padding: 0.5rem; + --mdc-text-button-container-height: 1.75rem; + font: var(--sys-label-small); + letter-spacing: var(--sys-label-small-tracking); + padding: 0.3125rem var(--mat-text-button-horizontal-padding); + } +} + // Toggle mat-button-toggle.mat-button-toggle-standalone.mat-button-toggle-appearance-standard { --mat-standard-button-toggle-shape: 0; - --mat-standard-button-toggle-hover-state-layer-opacity: 0.04; + --mat-standard-button-toggle-hover-state-layer-opacity: 0.08; --mat-standard-button-toggle-selected-state-text-color: var(--sys-secondary); + --mat-standard-button-toggle-disabled-state-text-color: color-mix(in srgb, var(--sys-on-surface) 40%, transparent); + --mat-standard-button-toggle-disabled-state-background-color: color-mix( + in srgb, + var(--sys-on-surface) 12%, + transparent + ); border: none; font: var(--sys-label-large); + letter-spacing: var(--sys-label-large-tracking); - .mat-button-toggle-label-content { - padding: 0 1rem; + &.button-size-large { + --mat-standard-button-toggle-height: 1.5rem; + .mat-button-toggle-label-content { + padding: 0.5rem 1rem; + } } - &[hraButtonSize='medium'] { + &.button-size-medium { + --mat-standard-button-toggle-height: 1.3125rem; + font: var(--sys-label-medium); + letter-spacing: var(--sys-label-medium-tracking); .mat-button-toggle-label-content { - padding: 0 0.5rem; + padding: 0.3438rem 0.5rem; } } &:active { .mat-button-toggle-focus-overlay { - opacity: 0.08; + opacity: 0.16; } } @@ -146,8 +239,7 @@ mat-button-toggle.mat-button-toggle-standalone.mat-button-toggle-appearance-stan } &.mat-button-toggle-checked:not(.nav-cat-button) { - --mat-standard-button-toggle-selected-state-text-color: var(--sys-on-primary); - background-color: var(--sys-tertiary); + background-color: color-mix(in srgb, var(--sys-tertiary) 24%, transparent); } } @@ -169,7 +261,8 @@ mat-button-toggle.mat-button-toggle-checked.nav-cat-button { } mat-button-toggle.mat-button-toggle-standalone.nav-cat-button { - font: var(--sys-title-small); + font: var(--sys-label-large); + letter-spacing: var(--sys-label-large-tracking); --mat-standard-button-toggle-hover-state-layer-opacity: 0; --mat-standard-button-toggle-selected-state-background-color: var(--sys-on-primary); :is(:hover, :active) { @@ -188,21 +281,3 @@ mat-button-toggle.mat-button-toggle-standalone.nav-cat-button { } } } - -// Nav Item -button[mat-button].nav-item-button { - --mat-text-button-hover-state-layer-opacity: 0; - --mat-text-button-pressed-state-layer-opacity: 0; - --mdc-text-button-container-shape: 0; - --mdc-text-button-label-text-color: var(--sys-secondary); - - &:is(:hover, :active) { - .label { - border-bottom: solid 1px var(--sys-tertiary); - } - } - - &:focus-visible { - outline: solid 2px var(--sys-tertiary); - } -} diff --git a/libs/design-system/button/src/lib/button.component.stories.ts b/libs/design-system/button/src/lib/button.component.stories.ts index 54dd74895..1a5f5bed9 100644 --- a/libs/design-system/button/src/lib/button.component.stories.ts +++ b/libs/design-system/button/src/lib/button.component.stories.ts @@ -24,93 +24,101 @@ type Story = StoryObj; export const BasicPrimary: Story = { args: { - size: 'large', + size: 'medium', + disabled: false, }, argTypes: { size: { control: 'select', - options: ['small', 'medium', 'large'], + options: ['small', 'medium'], + }, + disabled: { + control: 'boolean', }, }, render: (args) => ({ props: args, template: ` - + `, }), }; export const BasicSecondary: Story = { args: { - size: 'large', + size: 'medium', + disabled: false, }, argTypes: { size: { control: 'select', - options: ['small', 'medium', 'large'], + options: ['small', 'medium'], }, }, render: (args) => ({ props: args, template: ` - + `, }), }; -export const FlatPrimary: Story = { +export const BasicIcon: Story = { args: { - size: 'large', + disabled: false, + iconName: 'download', }, argTypes: { - size: { + disabled: { + control: 'boolean', + }, + iconName: { control: 'select', - options: ['large', 'medium'], + options: ['upload'], }, }, render: (args) => ({ props: args, template: ` - + `, }), }; -export const FlatSecondary: Story = { +export const FlatRound: Story = { args: { - size: 'large', + disabled: false, }, argTypes: { - size: { - control: 'select', - options: ['large', 'medium'], + disabled: { + control: 'boolean', }, }, render: (args) => ({ props: args, template: ` - + `, }), }; export const CtaFlatPrimary: Story = { args: { - size: 'large', + disabled: false, }, argTypes: { - size: { - control: 'select', - options: ['large', 'medium'], + disabled: { + control: 'boolean', }, }, render: (args) => ({ props: args, template: ` - `, @@ -124,19 +132,18 @@ export const CtaFlatPrimary: Story = { export const CtaFlatSecondary: Story = { args: { - size: 'large', + disabled: false, }, argTypes: { - size: { - control: 'select', - options: ['large', 'medium'], + disabled: { + control: 'boolean', }, }, render: (args) => ({ props: args, template: ` - `, @@ -158,7 +165,7 @@ export const ToggleButton: Story = { render: (args) => ({ props: args, template: ` - Button @@ -179,7 +186,7 @@ export const NavigationCategoryButton: Story = { render: (args) => ({ props: args, template: ` - + Button `, @@ -190,26 +197,3 @@ export const NavigationCategoryButton: Story = { ], }), }; - -export const NavigationItemButton: Story = { - args: { - size: 'large', - }, - argTypes: { - size: { - control: 'select', - options: ['large', 'medium'], - }, - }, - render: (args) => ({ - props: args, - template: ` - - `, - }), -}; diff --git a/libs/design-system/button/src/lib/button.module.ts b/libs/design-system/button/src/lib/button.module.ts index e403563ff..3c0334fba 100644 --- a/libs/design-system/button/src/lib/button.module.ts +++ b/libs/design-system/button/src/lib/button.module.ts @@ -4,7 +4,6 @@ import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { ButtonSizeDirective } from './button-size/button-size.directive'; import { CallToActionButtonDirective } from './directives/call-to-action-button.directive'; -import { NavigationItemButtonDirective } from './directives/nav-item-button.directive'; import { NavigationCategoryButtonDirective } from './directives/navigation-category-button.directive'; import { PrimaryButtonDirective } from './directives/primary-button.directive'; import { SecondaryButtonDirective } from './directives/secondary-button.directive'; @@ -16,7 +15,6 @@ import { SecondaryButtonDirective } from './directives/secondary-button.directiv PrimaryButtonDirective, SecondaryButtonDirective, NavigationCategoryButtonDirective, - NavigationItemButtonDirective, ButtonSizeDirective, ], exports: [ @@ -26,7 +24,6 @@ import { SecondaryButtonDirective } from './directives/secondary-button.directiv PrimaryButtonDirective, SecondaryButtonDirective, NavigationCategoryButtonDirective, - NavigationItemButtonDirective, ButtonSizeDirective, ], }) diff --git a/libs/design-system/button/src/lib/directives/nav-item-button.directive.ts b/libs/design-system/button/src/lib/directives/nav-item-button.directive.ts deleted file mode 100644 index 89ceb03de..000000000 --- a/libs/design-system/button/src/lib/directives/nav-item-button.directive.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Directive } from '@angular/core'; - -/** Directive for navigation item button */ -@Directive({ - selector: '[hraNavItemButton]', - standalone: true, - host: { - class: 'nav-item-button', - }, -}) -export class NavigationItemButtonDirective {} diff --git a/libs/design-system/color-picker/README.md b/libs/design-system/color-picker/README.md new file mode 100644 index 000000000..810e6309e --- /dev/null +++ b/libs/design-system/color-picker/README.md @@ -0,0 +1,3 @@ +# @hra-ui/design-system/color-picker + +Secondary entry point of `@hra-ui/design-system`. It can be used by importing from `@hra-ui/design-system/color-picker`. diff --git a/libs/design-system/color-picker/ng-package.json b/libs/design-system/color-picker/ng-package.json new file mode 100644 index 000000000..c781f0df4 --- /dev/null +++ b/libs/design-system/color-picker/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "src/index.ts" + } +} diff --git a/libs/design-system/color-picker/src/index.ts b/libs/design-system/color-picker/src/index.ts new file mode 100644 index 000000000..39eb43a96 --- /dev/null +++ b/libs/design-system/color-picker/src/index.ts @@ -0,0 +1,2 @@ +export * from './lib/color-picker.component'; +export * from './lib/color-utils'; diff --git a/libs/design-system/color-picker/src/lib/color-picker.component.html b/libs/design-system/color-picker/src/lib/color-picker.component.html new file mode 100644 index 000000000..f21a6cdb3 --- /dev/null +++ b/libs/design-system/color-picker/src/lib/color-picker.component.html @@ -0,0 +1,21 @@ + diff --git a/libs/design-system/color-picker/src/lib/color-picker.component.scss b/libs/design-system/color-picker/src/lib/color-picker.component.scss new file mode 100644 index 000000000..7276f9186 --- /dev/null +++ b/libs/design-system/color-picker/src/lib/color-picker.component.scss @@ -0,0 +1,192 @@ +:host { + display: flex; + height: 2rem; + align-items: center; + + .color-pick { + height: 1.25rem; + width: 1.25rem; + display: inline-block; + border-radius: 50%; + vertical-align: middle; + margin: 0.25rem 0.5rem 0.25rem 0.25rem; + position: relative; + cursor: pointer; + } +} + +::ng-deep { + color-picker { + div.color-picker { + border-radius: 0.5rem; + border: none; + box-shadow: 0px 5px 16px 0px #201e3d3d; + + .saturation-lightness { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; + } + + .hex-text { + padding: 0 0.75rem 1rem 0.75rem; + width: 86%; + + .box { + width: 100%; + align-items: center; + + &:not(:has(input)) { + padding: 0; + width: auto; + div { + --sys-label-small-tracking: 0.005em; + font: var(--sys-label-small); + color: var(--sys-secondary); + margin: 0 1.5rem; + margin-top: 0.25rem; + text-transform: uppercase; + } + } + + &:has(input) { + padding: 0; + margin-right: 0; + } + + input { + border-radius: 0.25rem; + height: 2rem; + padding: 0.5625rem 0; + font: var(--sys-label-small); + color: var(--sys-primary-fixed); + width: 90%; + border: solid 1px var(--sys-outline-variant); + text-transform: uppercase; + } + } + } + + .rgba-text, + .hsla-text { + .box { + padding-bottom: 0.25rem; + input { + border-radius: 0.25rem; + border-color: var(--sys-outline-variant); + max-width: 2.25rem; + height: 2rem; + } + + &:not(:has(input)) { + div { + max-width: 2.25rem; + font: var(--sys-label-small); + color: var(--sys-secondary); + } + } + + input { + font: var(--sys-label-small); + color: var(--sys-primary-fixed); + } + } + } + + .type-policy { + top: 13.25rem; + right: 1.5625rem; + } + + .hex-text, + .rgba-text, + .hsla-text { + padding-left: 1.5rem; + padding-top: 0; + } + + .button-area { + display: flex; + justify-content: space-between; + padding: 0; + margin: 0 0.75rem 0.75rem 0.75rem; + + .accept-button, + .cancel-button { + --sys-label-medium-tracking: 0.005em; + font: var(--sys-label-medium); + margin: 0; + border-radius: 0.25rem; + cursor: pointer; + border: none; + background-color: transparent; + padding: 0.3438rem 0.5rem; + &:focus-visible { + outline: solid 2px var(--sys-tertiary); + } + } + + .cancel-button { + &:hover { + background-color: color-mix(in srgb, var(--sys-secondary) 4%, transparent); + } + + &:active { + background-color: color-mix(in srgb, var(--sys-secondary) 8%, transparent); + } + } + + .accept-button { + color: var(--sys-on-tertiary-fixed); + + &:hover { + background-color: color-mix(in srgb, var(--sys-on-tertiary-fixed) 4%, transparent); + } + + &:active { + background-color: color-mix(in srgb, var(--sys-on-tertiary-fixed) 8%, transparent); + } + + &:focus-visible { + color: var(--sys-secondary); + } + } + } + .cursor { + border-color: white; + border-width: 0.25rem; + box-shadow: 0px 5px 16px 0px #201e3d3d; + } + + .selected-color { + border: none; + box-shadow: 1px 1px 4px 0px #201e3d3d; + top: 0; + left: 0; + } + + .hue-alpha { + padding: 0; + margin: 1rem 1.125rem; + + .left { + padding: 0; + } + + .right { + padding: 0.375rem 0 0.375rem 0.5rem; + } + + .cursor { + height: 0.5rem; + width: 0.5rem; + border-width: 0.125rem; + } + .hue, + .alpha { + border-radius: 0.25rem; + height: 0.5rem; + } + } + } + } +} diff --git a/libs/design-system/color-picker/src/lib/color-picker.component.spec.ts b/libs/design-system/color-picker/src/lib/color-picker.component.spec.ts new file mode 100644 index 000000000..ce0013355 --- /dev/null +++ b/libs/design-system/color-picker/src/lib/color-picker.component.spec.ts @@ -0,0 +1,30 @@ +import { render } from '@testing-library/angular'; +import { ColorPickerComponent } from './color-picker.component'; + +describe('ColorPickerComponent', () => { + it('should update color when selectColor is called', async () => { + const { + fixture: { componentInstance: instance }, + } = await render(ColorPickerComponent, { + componentInputs: { + color: [0, 0, 0], + }, + }); + + instance.selectColor('#ffffff'); + expect(instance.color()).toEqual([255, 255, 255]); + }); + + it('should not update color if same color picked', async () => { + const { + fixture: { componentInstance: instance }, + } = await render(ColorPickerComponent, { + componentInputs: { + color: [255, 255, 255], + }, + }); + + instance.selectColor('#ffffff'); + expect(instance.color()).toEqual([255, 255, 255]); + }); +}); diff --git a/libs/design-system/color-picker/src/lib/color-picker.component.ts b/libs/design-system/color-picker/src/lib/color-picker.component.ts new file mode 100644 index 000000000..84aeb9378 --- /dev/null +++ b/libs/design-system/color-picker/src/lib/color-picker.component.ts @@ -0,0 +1,32 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, model, output, signal } from '@angular/core'; +import { colorEquals, hexToRgb, Rgb } from './color-utils'; +import { ColorPickerDirective, ColorPickerModule } from 'ngx-color-picker'; + +/** Color Picker Component */ +@Component({ + selector: 'hra-color-picker', + standalone: true, + imports: [CommonModule, ColorPickerModule], + templateUrl: './color-picker.component.html', + styleUrl: './color-picker.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ColorPickerComponent { + /** The RGB color value */ + readonly color = model.required(); + + /** Emits when the color picker is opened or closed */ + readonly colorPickerOpen = output(); + + /** Hex representation of the color */ + readonly hexColor = signal('#000000'); + + /** Select a new color from the color picker */ + selectColor(hex: string): void { + const rgb = hexToRgb(hex); + if (!colorEquals(this.color(), rgb)) { + this.color.set(rgb); + } + } +} diff --git a/libs/design-system/color-picker/src/lib/color-picker.stories.ts b/libs/design-system/color-picker/src/lib/color-picker.stories.ts new file mode 100644 index 000000000..aeffafd84 --- /dev/null +++ b/libs/design-system/color-picker/src/lib/color-picker.stories.ts @@ -0,0 +1,32 @@ +import { applicationConfig, Meta, moduleMetadata, StoryObj } from '@storybook/angular'; +import { ColorPickerModule } from 'ngx-color-picker'; +import { ColorPickerComponent } from './color-picker.component'; + +const meta: Meta = { + component: ColorPickerComponent, + title: 'ColorPickerComponent', + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/design/BCEJn9KCIbBJ5MzqnojKQp/Explorer-Components?node-id=1117-10518', + }, + }, + decorators: [ + applicationConfig({ + providers: [], + }), + moduleMetadata({ + imports: [ColorPickerModule], + }), + ], + args: { + color: [170, 220, 223], + }, + render: (args) => ({ + props: args, + }), +}; +export default meta; +type Story = StoryObj; + +export const Primary: Story = {}; diff --git a/libs/cde-visualization/src/lib/models/color.ts b/libs/design-system/color-picker/src/lib/color-utils.ts similarity index 100% rename from libs/cde-visualization/src/lib/models/color.ts rename to libs/design-system/color-picker/src/lib/color-utils.ts diff --git a/libs/design-system/package.json b/libs/design-system/package.json index 0dd815606..dcd7c9d93 100644 --- a/libs/design-system/package.json +++ b/libs/design-system/package.json @@ -29,8 +29,9 @@ "@angular/core": "18.2.1", "@angular/material": "18.2.1", "ngx-scrollbar": "^15.1.0", - "@angular/cdk": "18.2.1", - "@angular/router": "18.2.1" + "ngx-color-picker": "^17.0.0", + "@angular/router": "18.2.1", + "@angular/cdk": "18.2.1" }, "dependencies": {}, "sideEffects": false diff --git a/libs/design-system/step-indicator/ng-package.json b/libs/design-system/step-indicator/ng-package.json new file mode 100644 index 000000000..c781f0df4 --- /dev/null +++ b/libs/design-system/step-indicator/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "src/index.ts" + } +} diff --git a/libs/design-system/step-indicator/src/index.ts b/libs/design-system/step-indicator/src/index.ts new file mode 100644 index 000000000..967c85dac --- /dev/null +++ b/libs/design-system/step-indicator/src/index.ts @@ -0,0 +1 @@ +export * from './lib/step-indicator.component'; diff --git a/libs/design-system/step-indicator/src/lib/step-indicator.component.html b/libs/design-system/step-indicator/src/lib/step-indicator.component.html new file mode 100644 index 000000000..c2a531f37 --- /dev/null +++ b/libs/design-system/step-indicator/src/lib/step-indicator.component.html @@ -0,0 +1 @@ +{{ value() }} diff --git a/libs/design-system/step-indicator/src/lib/step-indicator.component.scss b/libs/design-system/step-indicator/src/lib/step-indicator.component.scss new file mode 100644 index 000000000..1d23a5ce2 --- /dev/null +++ b/libs/design-system/step-indicator/src/lib/step-indicator.component.scss @@ -0,0 +1,14 @@ +:host { + .step-number { + font: var(--sys-label-large); + letter-spacing: var(--sys-label-large-tracking); + background-color: var(--sys-inverse-surface); + color: var(--sys-on-primary); + display: flex; + width: 2rem; + height: 2rem; + border-radius: 50%; + justify-content: center; + align-items: center; + } +} diff --git a/libs/design-system/step-indicator/src/lib/step-indicator.component.stories.ts b/libs/design-system/step-indicator/src/lib/step-indicator.component.stories.ts new file mode 100644 index 000000000..da4172c38 --- /dev/null +++ b/libs/design-system/step-indicator/src/lib/step-indicator.component.stories.ts @@ -0,0 +1,22 @@ +import { applicationConfig, Meta, StoryObj } from '@storybook/angular'; + +import { provideDesignSystem } from '../../../src/lib/providers'; +import { StepIndicatorComponent } from './step-indicator.component'; + +const meta: Meta = { + component: StepIndicatorComponent, + title: 'StepIndicatorComponent', + decorators: [ + applicationConfig({ + providers: [provideDesignSystem()], + }), + ], +}; +export default meta; +type Story = StoryObj; + +export const StepIndicator: Story = { + args: { + value: 1, + }, +}; diff --git a/libs/design-system/step-indicator/src/lib/step-indicator.component.ts b/libs/design-system/step-indicator/src/lib/step-indicator.component.ts new file mode 100644 index 000000000..b55cc2485 --- /dev/null +++ b/libs/design-system/step-indicator/src/lib/step-indicator.component.ts @@ -0,0 +1,18 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; + +/** + * Step indicator for module components that have multiple steps + */ +@Component({ + selector: 'hra-step-indicator', + standalone: true, + imports: [CommonModule], + templateUrl: './step-indicator.component.html', + styleUrl: './step-indicator.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class StepIndicatorComponent { + /** Step value */ + value = input.required(); +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 4740d4b44..0995373b6 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -83,6 +83,9 @@ "@hra-ui/design-system/button-toggle": [ "libs/design-system/button-toggle/src/index.ts" ], + "@hra-ui/design-system/color-picker": [ + "libs/design-system/color-picker/src/index.ts" + ], "@hra-ui/design-system/dialog": [ "libs/design-system/dialog/src/index.ts" ], @@ -119,6 +122,9 @@ "@hra-ui/design-system/social-media-button": [ "libs/design-system/social-media-button/src/index.ts" ], + "@hra-ui/design-system/step-indicator": [ + "libs/design-system/step-indicator/src/index.ts" + ], "@hra-ui/design-system/table": [ "libs/design-system/table/src/index.ts" ],