diff --git a/package.json b/package.json index 9ecd0cee7..cddf4f03c 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "sass": "^1.79.5", "storybook": "^7.4.1", "typescript": "^5.6.3", - "ucla-library-design-tokens": "^5.27.1", + "ucla-library-design-tokens": "^5.28.0", "video.js": "^8.5.2", "vite": "^5.4.8", "vite-svg-loader": "^5.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cee8fcc41..18ded3350 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -121,8 +121,8 @@ importers: specifier: ^5.6.3 version: 5.6.3 ucla-library-design-tokens: - specifier: ^5.27.1 - version: 5.27.2 + specifier: ^5.28.0 + version: 5.28.0 video.js: specifier: ^8.5.2 version: 8.19.1 @@ -5958,8 +5958,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - ucla-library-design-tokens@5.27.2: - resolution: {integrity: sha512-+GgHevX1Fflu1uSAgT7otxzqLUuOAXaVKK9vKqTckSiSMxMs1dqBBXqA6+++HuPym2MazR0lSBHA8B53cfVk1Q==} + ucla-library-design-tokens@5.28.0: + resolution: {integrity: sha512-qgxGlK3/m0VwYGumzLZTTB/ae03DY41+80vFhm4+gukzh8XiqUsN71cg3ijk23Dl9awyicoELbRgnUV4nj3e0g==} ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} @@ -13128,7 +13128,7 @@ snapshots: typescript@5.6.3: {} - ucla-library-design-tokens@5.27.2: {} + ucla-library-design-tokens@5.28.0: {} ufo@1.5.4: {} diff --git a/src/lib-components/FiltersDropdown.vue b/src/lib-components/FiltersDropdown.vue new file mode 100644 index 000000000..350bcef12 --- /dev/null +++ b/src/lib-components/FiltersDropdown.vue @@ -0,0 +1,114 @@ + + + + + + + + Filters ({{ numOfSelectedFilters }} selected ) + + + + + + + + + {{ group.name }} + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib-components/MobileDrawer.vue b/src/lib-components/MobileDrawer.vue index 2bfb29c22..d90548cba 100644 --- a/src/lib-components/MobileDrawer.vue +++ b/src/lib-components/MobileDrawer.vue @@ -44,7 +44,7 @@ onMounted(() => { - + { + it('Default', () => { + cy.visit( + '/iframe.html?id=filters-dropdown--default&args=&viewMode=story' + ) + cy.get('.filters-dropdown').should('exist') + + cy.percySnapshot('FiltersDropdown: Default') + }) +}) diff --git a/src/stories/FiltersDropdown.stories.js b/src/stories/FiltersDropdown.stories.js new file mode 100644 index 000000000..310cd9f3f --- /dev/null +++ b/src/stories/FiltersDropdown.stories.js @@ -0,0 +1,116 @@ +import { computed, onMounted, ref } from 'vue' +import FiltersDropdown from '@/lib-components/FiltersDropdown.vue' + +export default { + title: 'Filters Dropdown', + component: FiltersDropdown, +} + +// MOCK DATA +const mockFilterGroups = [ + { + name: 'Event Type', + searchField: 'ftvaEventTypeFilters.title.keyword', + options: ['Film', 'Theater', 'Lecture'], + }, + { + name: 'Screen Format', + searchField: 'ftvaScreeningFormatFilters.title.keyword', + options: ['Online', 'In-Person'], + }, +] +// note that because this component uses v-model by default, +// parent component needs to use a ref for selectedFilters +const mockEmptySelectedFilters = ref({ 'ftvaEventTypeFilters.title.keyword': [], 'ftvaScreeningFormatFilters.title.keyword': [] }) +const mockSelectedFilters = ref({ + 'ftvaEventTypeFilters.title.keyword': ['Film', 'Theater'], + 'ftvaScreeningFormatFilters.title.keyword': ['Online'], +}) + +export function Default() { + return { + components: { FiltersDropdown }, + data() { + return { mockFilterGroups, mockSelectedFilters: mockEmptySelectedFilters } + }, + template: 'Selected filters display:{{ mockSelectedFilters }}', + } +} + +// uses async data +export function InitialSelectedFilters() { + return { + components: { FiltersDropdown }, + setup() { + const selectedFilters = ref({}) + // mock getting selected filters from a route or other async source + const fetchFilters = async () => { + // Mocking an async fetch call + const response = await new Promise((resolve) => { + setTimeout(() => { + resolve(mockSelectedFilters.value) + }, 1000) + }) + selectedFilters.value = response + } + + fetchFilters() + + return { selectedFilters } + }, + data() { + return { mockFilterGroups } + }, + template: 'Selected filters display:{{ selectedFilters }}', + } +} + +// FTVA Theme +export function FTVA() { + return { + components: { FiltersDropdown }, + data() { + return { mockFilterGroups, mockSelectedFilters } + }, + provide() { + return { + theme: computed(() => 'ftva'), + } + }, + template: 'Selected filters display:{{ mockSelectedFilters }}', + } +} + +// FTVA Theme W Selected Filters updating on 'done' click only +// This is the current planned implmentation on the FTVA site +const mockSelectedFiltersEmitted = ref({ + 'ftvaEventTypeFilters.title.keyword': ['Film'], + 'ftvaScreeningFormatFilters.title.keyword': ['Online'], +}) +export function FTVAFiltersUpdateDoneClick() { + return { + components: { FiltersDropdown }, + setup() { + const selectedFiltersDisplay = ref({}) + + const updateFiltersDisplay = () => { + // assignment is done with spread operator so that a copy is made + selectedFiltersDisplay.value = { ...mockSelectedFiltersEmitted.value } + } + onMounted(() => { + // trigger function once onMount to update display with initial selected filters + updateFiltersDisplay() + }) + return { selectedFiltersDisplay, updateFiltersDisplay } + }, + data() { + return { mockFilterGroups, mockSelectedFiltersEmitted } + }, + provide() { + return { + theme: computed(() => 'ftva'), + } + }, + template: 'Selected filters display:{{ selectedFiltersDisplay }}', + } +} diff --git a/src/styles/default/_filters-dropdown.scss b/src/styles/default/_filters-dropdown.scss new file mode 100644 index 000000000..f1acef41e --- /dev/null +++ b/src/styles/default/_filters-dropdown.scss @@ -0,0 +1,126 @@ +.filters-dropdown { + // Dropdown Window Size & Input + :deep(.mobile-button) { + min-width: 380px; + .toggle-triangle-icon { + path.svg__fill--accent-blue { + fill: $accent-blue; + } + } + .button-inner-wrapper { + width: 100%; + justify-content: space-between; + .icon-svg { + align-self: flex-end; + padding-right: 10px; + margin-bottom: -5px; + } + } + } + + :deep(.dropdown-wrapper) { + width: 100%; // filters input grows to container width + } + :deep(.button-dropdown-modal-wrapper.is-expanded) { + min-width: 380px; + margin: auto; + padding: 24px 30px 30px 30px; + width: 100%; // filters dropdown grows to container width + } + + // Filter Heading + .filter-group { + h3 { + text-transform: uppercase; + @include ftva-subtitle-1; + color: $heading-grey; + } + } + + // Filter Pills + .pills { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin: 8px auto 20px; + + .pill-label { + display: inline-flex; + position: relative; + + .pill-checkbox { + opacity: 0; + position: absolute; + } + } + .close-icon { + margin: 5px 0 5px 5px; + } + } + + :deep(.block-tag) { + cursor: pointer; + } + + // Selected Filter Pills + :deep(.block-tag:hover), :deep(.block-tag:focus) + .pill-checkbox:checked+ :deep(.block-tag) { + background-color: var(--color-primary-blue-03); + + .label { + color: white; + } + } + + // Done / Clear Buttons + .action-row { + display: flex; + justify-content: flex-end; + width: 100%; + align-items: center; + padding-top: 18px; + gap: 5px; + + .action-row-button { + border-radius: 2px; + font-family: var(--font-primary); + font-weight: 500; + line-height: 27px; + color: var(--color-white); + background-color: var(--color-primary-blue-03); + border: 1px solid var(--color-primary-blue-03); + cursor: pointer; + transition: background-color 0.3s ease; + + &.select-button> :deep(.label) { + padding-left: 0px; + } + + :deep(.hover) { + display: none; + } + + &:hover, &:focus { + background-color: $navy-blue; + } + } + + .clear-button { + background-color: var(--color-white); + color: var(--color-primary-blue-03); + + :deep(.svg__stroke--primary-blue-03) { + stroke: var(--color-primary-blue-03); + } + + &:hover, &:focus { + background-color: $navy-blue; + color: var(--color-white); + + :deep(.svg__stroke--primary-blue-03) { + stroke: var(--color-white); + } + } + } + } +} diff --git a/src/styles/ftva/_filters-dropdown.scss b/src/styles/ftva/_filters-dropdown.scss new file mode 100644 index 000000000..de056ce03 --- /dev/null +++ b/src/styles/ftva/_filters-dropdown.scss @@ -0,0 +1,42 @@ +.ftva.filters-dropdown { + .action-row { + .action-row-button { + color: $accent-blue; + border-color: $accent-blue; + &.select-button { + background-color: $accent-blue; + color: white; + } + &.clear-button { + color: $accent-blue; + :deep(.svg__stroke--primary-blue-03) { + stroke: $accent-blue; + } + &:hover, &:focus { + background-color: $navy-blue; + color: var(--color-white); + :deep(.svg__stroke--primary-blue-03) { + stroke: var(--color-white); + } + } + } + } + } + // Selected Filter Pills + :deep(.block-tag:hover), :deep(.block-tag:focus), + .pill-checkbox:checked+ :deep(.block-tag) { + background-color: $accent-blue; + border-color: $accent-blue; + .label { + color: white; + } + } + :deep(.svg__icon-close) { + path { + stroke: white; + } + circle { + fill: transparent; + } + } +} \ No newline at end of file