diff --git a/drupalci.yml b/drupalci.yml index 971fd83b6e8bc52857c74a53e107f2e01ec9e8de..7e32b3c2f8a98f88146a56d3a36651b3d7889b85 100644 --- a/drupalci.yml +++ b/drupalci.yml @@ -3,7 +3,7 @@ build: assessment: validate_codebase: {} - # Project Browser code quality checking matches that of Drupal core: it is checked by container_command.commit_checks. + # Project Browser code quality checking matches that of Drupal core: it is checked by container_command.commit_checks. testing: # Run code quality checks. container_command.commit-checks: @@ -29,12 +29,14 @@ build: - sed -i "s/core\/assets\/vendor/sveltejs\/public\/build/" modules/contrib/project_browser/commit-code-check.sh # Stop not requiring ES6 files for core's Nightwatch tests, instead don't for *our* Nightwatch tests. - sed -i "s/\^core\/tests\/Drupal\/Nightwatch/\^tests\/src\/Nightwatch/" modules/contrib/project_browser/commit-code-check.sh - - sed -i "s/.eslintrc.passing.json/DISABLED.eslintrc.passing.json/" modules/contrib/project_browser/commit-code-check.sh + # Remove check for changed .js files to avoid running core eslint. There is svelte specific + # linting run instead. + - sed -i "s/FILE =~.*\.js\$ ]]; then/FILE =~ \\.noopjs$ ]]; then/" modules/contrib/project_browser/commit-code-check.sh # Disable default JS compile checks, PB will be doing a hacky version - sed -i "s/COMPILE_CHECK=1/COMPILE_CHECK=0/" modules/contrib/project_browser/commit-code-check.sh - sed -i "s/core\/scripts\/js/\(js|scripts|modules|project_browser_plugin_starter_template\)/" modules/contrib/project_browser/commit-code-check.sh - sed -i "s/\^core\/scripts\/css/config.js\$/" modules/contrib/project_browser/commit-code-check.sh - # Run eslint on .svelte files. + # Run eslint on .svelte files. - sed -i '/.*FINAL_STATUS" == "1".*/i cd "$TOP_LEVEL\/sveltejs"' modules/contrib/project_browser/commit-code-check.sh - sed -i '/.*FINAL_STATUS" == "1".*/i yarn install' modules/contrib/project_browser/commit-code-check.sh - sed -i '/.*FINAL_STATUS" == "1".*/i yarn lint:svelte' modules/contrib/project_browser/commit-code-check.sh diff --git a/sveltejs/public/build/bundle.css b/sveltejs/public/build/bundle.css index a8f11ae896f8c66d59bbec6bf160d3cf2965fe1c..a294b9daea92e4db146bf6306386e836ae68ba9d 100644 Binary files a/sveltejs/public/build/bundle.css and b/sveltejs/public/build/bundle.css differ diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js index 2656adba1c79f67fcdf6a4039f1f19473a18d9b5..5a783ff35d90447604d4d37f4625636699ef7712 100644 Binary files a/sveltejs/public/build/bundle.js and b/sveltejs/public/build/bundle.js differ diff --git a/sveltejs/public/build/bundle.js.map b/sveltejs/public/build/bundle.js.map index 99f13659a63de835c252a13bcde9aaa09ffc3566..e9d7d53a6334548e82d378e60ca083290d250a20 100644 Binary files a/sveltejs/public/build/bundle.js.map and b/sveltejs/public/build/bundle.js.map differ diff --git a/sveltejs/public/index.html b/sveltejs/public/index.html index 1a9838e76d2786536119dd09d10ad79de96b0fe5..f515e22053bfe70fb31d60486a40d35f45ad1f25 100644 --- a/sveltejs/public/index.html +++ b/sveltejs/public/index.html @@ -11,7 +11,7 @@ <script defer src='/build/bundle.js'></script> </head> <body> -<div id="project-browser"> +<div id="project-browser" class="project-browser"> </div> </body> </html> diff --git a/sveltejs/src/ModulePage.svelte b/sveltejs/src/ModulePage.svelte index 711ad1a20d177f336bfb4c83effad76a23e74b90..07997953e733ba82574ecc2e981a4c4b6e0f5922 100644 --- a/sveltejs/src/ModulePage.svelte +++ b/sveltejs/src/ModulePage.svelte @@ -51,8 +51,8 @@ <ul class="module-page__category-list" aria-labelledby="categories"> {#each project.module_categories || [] as category} <li - on:click={filterByCategory(category.id)} - class="module-page__category-list-item on" + on:click={() => filterByCategory(category.id)} + class="module-page__category-list-item" > {category.name} </li> @@ -109,10 +109,7 @@ <ImageCarousel sources={project.project_images} /> </div> {/if} - <div - class="module-page__wrapper__box-2--project-description" - id="description-wrapper" - > + <div class="module-page__project-description" id="description-wrapper"> {@html project.body.value} </div> </div> @@ -167,7 +164,7 @@ height: 20px; cursor: pointer; } - .module-page__category-list-item.on { + .module-page__category-list-item { list-style: none; display: inline-block; margin-top: 5px; diff --git a/sveltejs/src/Project/ProjectButtonBase.svelte b/sveltejs/src/Project/ProjectButtonBase.svelte index 92d3ec06eaa4265d76ca6edef0a15b8c1da59dd1..0d4bb4fd88eeb70eeb26c2994aa850913c20c66d 100644 --- a/sveltejs/src/Project/ProjectButtonBase.svelte +++ b/sveltejs/src/Project/ProjectButtonBase.svelte @@ -1,12 +1,12 @@ <script> // eslint-disable-next-line import/prefer-default-export - export let click; + export let click = () => {}; </script> <button + on:click={click} class="button button--primary project_button--primary" {...$$restProps} - on:click={click} > <slot /> </button> diff --git a/sveltejs/src/ProjectBrowser.svelte b/sveltejs/src/ProjectBrowser.svelte index 5d91d92ff514e852d67831c2946b3a46b0911860..b6b621902dfcbd412dc1dd680f886e0aa32cc8fc 100644 --- a/sveltejs/src/ProjectBrowser.svelte +++ b/sveltejs/src/ProjectBrowser.svelte @@ -247,14 +247,7 @@ </script> <MediaQuery query="(min-width: 1200px)" let:matches> - <ProjectGrid - class="project-browser" - {loading} - {rows} - {pageIndex} - {$pageSize} - let:rows - > + <ProjectGrid {loading} {rows} {pageIndex} {$pageSize} let:rows> <div slot="top"> <Search on:search={onSearch} @@ -335,9 +328,7 @@ {/if} <Pagination page={$page} - {$pageSize} count={$rowsCount} - serverSide={true} on:pageChange={onPageChange} on:pageSizeChange={onPageSizeChange} /> @@ -355,9 +346,7 @@ <div slot="bottom"> <Pagination page={$page} - {$pageSize} count={$rowsCount} - serverSide={true} on:pageChange={onPageChange} on:pageSizeChange={onPageSizeChange} /> diff --git a/sveltejs/src/ProjectGrid.svelte b/sveltejs/src/ProjectGrid.svelte index 7ed56c76cb349be53a755adbb2e8db3ea8f828c1..b3c023ceb325570e396d34ac341fd8bb5272aad8 100644 --- a/sveltejs/src/ProjectGrid.svelte +++ b/sveltejs/src/ProjectGrid.svelte @@ -1,5 +1,5 @@ <script context="module"> - import Search from './Search.svelte'; + import Search from './Search/Search.svelte'; import Filter from './Filter.svelte'; import Loading from './Loading.svelte'; import { pageSize } from './stores'; @@ -8,9 +8,8 @@ </script> <script> - import { createEventDispatcher, setContext } from 'svelte'; + import { setContext } from 'svelte'; - const dispatch = createEventDispatcher(); const { Drupal } = window; export let loading = false; @@ -43,38 +42,23 @@ filteredRows = _rows; }, }); - - function onSearch(event) { - dispatch('search', event.detail); - } - - function onSelectCategory(event) { - dispatch('selectCategory', event.detail); - } </script> <!--Aligns Category filter and Grid cards side by side--> - -<div class="container-1"> - <div class="box-1"> - <slot name="left"> - <svelte:component this={Filter} on:selectCategory={onSelectCategory} /> - </slot> - </div> - <div class="box-2"> - <slot name="top"> - <div class="slot-top"> - <svelte:component this={Search} on:search={onSearch} /> - </div> - </slot> - <div class={`${$$props.class}`}> +<div class="project-browser__container"> + <aside class="project-browser__aside"> + <slot name="left" /> + </aside> + <div class="project-browser__main"> + <slot name="top" /> + <div class="projects-container"> <slot name="head" /> {#if loading} <Loading /> {:else if visibleRows.length === 0} <div>{@html labels.empty}</div> {:else} - <ul> + <ul class="projects-list"> <slot rows={visibleRows} /> </ul> {/if} @@ -86,34 +70,28 @@ <slot name="bottom" /> <style> - ul { + .projects-list { display: flex; flex-wrap: wrap; list-style-type: none; margin: 0; } - .slot-top, - .slot-bottom { - width: 100%; - margin-top: 1em; + + .project-browser__aside { + flex: 1; + } + .project-browser__main { + flex: 4; } + @media (min-width: 700px) { - .container-1 { + .project-browser__container { display: flex; } } - .container-1 div { - padding: 10px; - } - .box-1 { - flex: 1; - } - .box-2 { - flex: 4; - } /* Stack cards below category filter on smaller screens */ @media screen and (max-width: 900px) { - .container-1 { + .project-browser__container { flex-direction: column; } } diff --git a/sveltejs/src/FilterApplied.svelte b/sveltejs/src/Search/FilterApplied.svelte similarity index 95% rename from sveltejs/src/FilterApplied.svelte rename to sveltejs/src/Search/FilterApplied.svelte index 0006efb28c83b8bcca7921914b3358e773ebf7cd..5803d779342c88dddf1784f49670120e65c557fa 100644 --- a/sveltejs/src/FilterApplied.svelte +++ b/sveltejs/src/Search/FilterApplied.svelte @@ -1,5 +1,5 @@ <script> - import { ALL_VALUES_ID, FULL_MODULE_PATH } from './constants'; + import { ALL_VALUES_ID, FULL_MODULE_PATH } from '../constants'; export let id; export let label; diff --git a/sveltejs/src/FilterGroup.svelte b/sveltejs/src/Search/FilterGroup.svelte similarity index 98% rename from sveltejs/src/FilterGroup.svelte rename to sveltejs/src/Search/FilterGroup.svelte index 3e6ccf7ce8ec87e83d02712677c3ab1395dc6df0..4e04636619d5e8aae4fa28526eadfa036818b37e 100644 --- a/sveltejs/src/FilterGroup.svelte +++ b/sveltejs/src/Search/FilterGroup.svelte @@ -1,10 +1,10 @@ <script> + import { filters } from '../stores'; + export let filterTitle; export let filterData; export let changeHandler; export let filterType; - - import { filters } from './stores'; </script> <div diff --git a/sveltejs/src/Search.svelte b/sveltejs/src/Search/Search.svelte similarity index 56% rename from sveltejs/src/Search.svelte rename to sveltejs/src/Search/Search.svelte index e02cdb7bfc6a11dda02a57f1fcfa1a56819e2192..565f1705a5ca9084dd7b71038d33011ff5903ab6 100644 --- a/sveltejs/src/Search.svelte +++ b/sveltejs/src/Search/Search.svelte @@ -1,10 +1,10 @@ <script> import { createEventDispatcher, getContext, onMount } from 'svelte'; - import { slide } from 'svelte/transition'; - import FilterGroup from './FilterGroup.svelte'; import FilterApplied from './FilterApplied.svelte'; - import { normalizeOptions, shallowCompare } from './util'; - import ProjectIcon from './Project/ProjectIcon.svelte'; + import { normalizeOptions, shallowCompare } from '../util'; + import SearchFilters from './SearchFilters.svelte'; + import SearchFilterToggle from './SearchFilterToggle.svelte'; + import SearchSort from './SearchSort.svelte'; import { filters, rowsCount, @@ -14,7 +14,7 @@ sort, searchString, sortCriteria, - } from './stores'; + } from '../stores'; import { COVERED_ID, ACTIVELY_MAINTAINED_ID, @@ -24,7 +24,7 @@ ALL_VALUES_ID, FULL_MODULE_PATH, DARK_COLOR_SCHEME, - } from './constants'; + } from '../constants'; const { Drupal } = window; const { announce } = Drupal; @@ -45,7 +45,8 @@ placeholder: Drupal.t('Module Name, Keyword(s), etc.'), }; - let isOpen = false; + // eslint-disable-next-line prefer-const + let filtersOpen = false; let sortMatch = $sortCriteria.find((option) => option.id === $sort); if (typeof sortMatch === 'undefined') { $sort = $sortCriteria[0].id; @@ -124,24 +125,7 @@ refreshLiveRegion(); } - async function onSort(event) { - const state = stateContext.getState(); - const detail = { - originalEvent: event, - page: state.page, - pageIndex: state.pageIndex, - pageSize: state.pageSize, - rows: state.filteredRows, - sort: $sort, - }; - dispatch('sort', detail); - stateContext.setPage(0, 0); - stateContext.setRows(detail.rows); - sortText = $sortCriteria.find((option) => option.id === $sort).text; - refreshLiveRegion(); - } - - async function onAdvancedFilter(event) { + const onAdvancedFilter = async (event) => { const state = stateContext.getState(); const detail = { originalEvent: event, @@ -157,13 +141,7 @@ stateContext.setPage(0, 0); stateContext.setRows(detail.rows); refreshLiveRegion(); - } - - /* When the user clicks on the button, - toggle between hiding and showing the dropdown content */ - function openDropdown() { - isOpen = !isOpen; - } + }; function onSelectCategory(event) { const state = stateContext.getState(); @@ -185,6 +163,25 @@ $filters = $filters; onAdvancedFilter(); } + + /** + * Actions performed when clicking filter resets such as "recommended" + * @param {string} maintenanceId + * ID of the selected maintenance status. + * @param {string} developmentId + * ID of the selected development status. + * @param {string} securityId + * ID of the selected security status. + */ + const filterResets = (maintenanceId, developmentId, securityId) => { + $filters.maintenanceStatus = maintenanceId; + $filters.developmentStatus = developmentId; + $filters.securityCoverage = securityId; + $filters = $filters; + $moduleCategoryFilter = []; + onAdvancedFilter(); + onSelectCategory(); + }; </script> <form class="search__form"> @@ -229,36 +226,15 @@ >{Drupal.t('Sorted by @sortText', { '@sortText': sortText })}</span > </span> - - {#if $filters.developmentStatus} - <FilterApplied - id={$filters.developmentStatus} - label={$filtersVocabularies.developmentStatus[ - $filters.developmentStatus - ]} - clickHandler={() => removeFilter('developmentStatus')} - /> - {/if} - - {#if $filters.maintenanceStatus} - <FilterApplied - id={$filters.maintenanceStatus} - label={$filtersVocabularies.maintenanceStatus[ - $filters.maintenanceStatus - ]} - clickHandler={() => removeFilter('maintenanceStatus')} - /> - {/if} - - {#if $filters.securityCoverage} - <FilterApplied - id={$filters.securityCoverage} - label={$filtersVocabularies.securityCoverage[ - $filters.securityCoverage - ]} - clickHandler={() => removeFilter('securityCoverage')} - /> - {/if} + {#each ['developmentStatus', 'maintenanceStatus', 'securityCoverage'] as filterType} + {#if $filters[filterType]} + <FilterApplied + id={$filters[filterType]} + label={$filtersVocabularies[filterType][$filters[filterType]]} + clickHandler={() => removeFilter(filterType)} + /> + {/if} + {/each} {#each $moduleCategoryFilter as category} <FilterApplied @@ -278,15 +254,8 @@ {#if $filters.securityCoverage !== ALL_VALUES_ID || $filters.maintenanceStatus !== ALL_VALUES_ID || $filters.developmentStatus !== ALL_VALUES_ID || $moduleCategoryFilter.length} <button class="search__filter-button" - on:click={() => { - $filters.maintenanceStatus = ALL_VALUES_ID; - $filters.developmentStatus = ALL_VALUES_ID; - $filters.securityCoverage = ALL_VALUES_ID; - $filters = $filters; - $moduleCategoryFilter = []; - onAdvancedFilter(); - onSelectCategory(); - }} + on:click|preventDefault={() => + filterResets(ALL_VALUES_ID, ALL_VALUES_ID, ALL_VALUES_ID)} > {Drupal.t('Clear filters')} </button> @@ -294,115 +263,19 @@ {#if !($filters.maintenanceStatus === ACTIVELY_MAINTAINED_ID && $filters.securityCoverage === COVERED_ID && $filters.developmentStatus === ALL_VALUES_ID && $moduleCategoryFilter.length === 0)} <button class="search__filter-button" - on:click={() => { - $filters.maintenanceStatus = ACTIVELY_MAINTAINED_ID; - $filters.securityCoverage = COVERED_ID; - $filters.developmentStatus = ALL_VALUES_ID; - $moduleCategoryFilter = []; - $filters = $filters; - onAdvancedFilter(); - onSelectCategory(); - }} + on:click|preventDefault={() => + filterResets(ACTIVELY_MAINTAINED_ID, ALL_VALUES_ID, COVERED_ID)} > {Drupal.t('Recommended filters')} </button> {/if} </div> </section> - <div class="search__sort"> - <label for="pb-sort">{Drupal.t('Sort by:')}</label> - <select - name="pb-sort" - id="pb-sort" - bind:value={$sort} - on:change={onSort} - class="search__sort-select form-select form-element form-element--type-select" - > - {#each $sortCriteria as opt} - <option value={opt.id}> - {opt.text} - </option> - {/each} - </select> - </div> - <div class="search__filter"> - <section aria-label={Drupal.t('Filter settings')}> - <button - type="button" - class="search__filter__toggle form-element" - class:is_open={isOpen} - aria-controls="filter-dropdown" - aria-label={isOpen - ? Drupal.t('Close Filter') - : Drupal.t('Open Filter')} - aria-expanded={isOpen.toString()} - on:click={() => openDropdown()} - ><img - src="{FULL_MODULE_PATH}/images/advanced-filter-icon.svg" - alt="advanced filter icon" - />Filters - </button> - </section> - </div> + <SearchSort on:sort bind:sortText refresh={refreshLiveRegion} /> + <SearchFilterToggle bind:isOpen={filtersOpen} /> </div> <div class="search__dropdown dropdown-filters" id="filter-dropdown"> - {#if isOpen} - <div class="search__filters" transition:slide> - <FilterGroup - filterTitle={Drupal.t('Development Status')} - filterData={DEVELOPMENT_OPTIONS} - filterType="developmentStatus" - changeHandler={onAdvancedFilter} - let:id - let:label - > - <label - slot="label" - class="search__checkbox-label" - for={`developmentStatus${id}`} - > - {label} - </label> - </FilterGroup> - <FilterGroup - filterTitle={Drupal.t('Maintenance Status')} - filterData={MAINTENANCE_OPTIONS} - filterType="maintenanceStatus" - changeHandler={onAdvancedFilter} - let:id - let:label - > - <label - slot="label" - class="search__checkbox-label" - for={`maintenanceStatus${id}`} - > - {label} - </label> - </FilterGroup> - <FilterGroup - filterTitle={Drupal.t('Security Advisory Coverage')} - filterData={SECURITY_OPTIONS} - filterType="securityCoverage" - changeHandler={onAdvancedFilter} - let:id - let:label - > - <label - slot="label" - class="search__checkbox-label" - for={`securityCoverage${id}`} - > - {label} - {#if id === COVERED_ID} - <span class="small-icons"> - <ProjectIcon type="status" /> - </span> - {/if} - </label> - </FilterGroup> - </div> - {/if} + <SearchFilters bind:isOpen={filtersOpen} {onAdvancedFilter} /> </div> </form> @@ -445,7 +318,7 @@ inset-inline-end: 30px; } - ::placeholder { + .search__searchterm::placeholder { font-family: sans-serif; font-style: normal; font-weight: 400; @@ -455,39 +328,12 @@ align-items: center; } - .search__filter__toggle > img { - width: 20px; - height: 14px; - margin-bottom: -2px; - margin-inline-end: 4px; - } - - .search__filter__toggle.is_open { - background-color: #adaeb3; - } - .dropdown-filters { position: relative; border: 3px solid #f3f4f9; z-index: 1; } - .small-icons { - margin-top: -3px; - margin-inline-end: 1px; - } - - .search__checkbox-label { - font-weight: normal; - padding-inline-start: 35px; - display: flex; - } - - .form-select:hover, - .search__filter__toggle:hover { - cursor: pointer; - } - #output { display: inline-block; font-family: sans-serif; @@ -509,31 +355,6 @@ max-width: 100%; } - .search__sort-select { - border: none; - background-color: #d3d4d9; - } - - .search__sort { - z-index: 2; - } - - .search__filter__toggle { - background-color: #d3d4d9; - color: black; - padding-left: 16px; - padding-right: 16px; - font-size: 16px; - border-radius: 2px; - width: fit-content; - margin-inline-start: 15px; - text-decoration: underline; - } - - .search__filter__toggle > img { - padding: 0 0.25rem; - } - .search__filter-button { padding: 0 0.25rem; background: none; @@ -543,41 +364,9 @@ cursor: pointer; } - .search__filter { - display: flex; - margin-inline-end: 1em; - } - - @media only screen and (min-width: 1200px) { - .search__checkbox-label { - font-weight: normal; - padding-inline-start: 35px; - display: flex; - margin-right: unset; - } - } - @media screen and (max-width: 855px) { .search__grid-container { display: block; } - .search__sort { - margin-bottom: 10px; - margin-top: 10px; - } - } - - @media (forced-colors: active) { - .search__filter__toggle { - border: 1px solid; - } - #pb-sort { - border: 1px solid; - } - @media (prefers-color-scheme: dark) { - .search__filter__toggle > img { - filter: invert(1); - } - } } </style> diff --git a/sveltejs/src/Search/SearchFilterToggle.svelte b/sveltejs/src/Search/SearchFilterToggle.svelte new file mode 100644 index 0000000000000000000000000000000000000000..4dd3e291690d26a4286274693375cce18229ce24 --- /dev/null +++ b/sveltejs/src/Search/SearchFilterToggle.svelte @@ -0,0 +1,76 @@ +<script> + import { FULL_MODULE_PATH } from '../constants'; + + const { Drupal } = window; + + // eslint-disable-next-line import/prefer-default-export + export let isOpen; + + /* When the user clicks on the button, + toggle between hiding and showing the dropdown content */ + function openDropdown() { + isOpen = !isOpen; + } +</script> + +<div class="search__filter__toggle-container"> + <section aria-label={Drupal.t('Filter settings')}> + <button + type="button" + class="search__filter__toggle form-element" + class:is_open={isOpen} + aria-controls="filter-dropdown" + aria-label={isOpen ? Drupal.t('Close Filter') : Drupal.t('Open Filter')} + aria-expanded={isOpen.toString()} + on:click={() => openDropdown()} + ><img + src="{FULL_MODULE_PATH}/images/advanced-filter-icon.svg" + alt="advanced filter icon" + class="search__filter__toggle-img" + />Filters + </button> + </section> +</div> + +<style> + .search__filter__toggle-container { + display: flex; + margin-inline-end: 1em; + } + + .search__filter__toggle { + background-color: #d3d4d9; + color: black; + padding-left: 16px; + padding-right: 16px; + font-size: 16px; + border-radius: 2px; + width: fit-content; + margin-inline-start: 15px; + text-decoration: underline; + } + .search__filter__toggle.is_open { + background-color: #adaeb3; + } + .search__filter__toggle:hover { + cursor: pointer; + } + + .search__filter__toggle-img { + width: 20px; + height: 14px; + padding: 0 0.25rem; + margin-bottom: -2px; + margin-inline-end: 4px; + } + @media (forced-colors: active) { + .search__filter__toggle { + border: 1px solid; + } + @media (prefers-color-scheme: dark) { + .search__filter__toggle-img { + filter: invert(1); + } + } + } +</style> diff --git a/sveltejs/src/Search/SearchFilters.svelte b/sveltejs/src/Search/SearchFilters.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e41232cc91517fd6d2e264149fd5d82ab1f8a9bf --- /dev/null +++ b/sveltejs/src/Search/SearchFilters.svelte @@ -0,0 +1,94 @@ +<script> + import { slide } from 'svelte/transition'; + import FilterGroup from './FilterGroup.svelte'; + import ProjectIcon from '../Project/ProjectIcon.svelte'; + import { + COVERED_ID, + MAINTENANCE_OPTIONS, + DEVELOPMENT_OPTIONS, + SECURITY_OPTIONS, + } from '../constants'; + + const { Drupal } = window; + + export let onAdvancedFilter; + export let isOpen; +</script> + +{#if isOpen} + <div class="search__filters" transition:slide> + <FilterGroup + filterTitle={Drupal.t('Development Status')} + filterData={DEVELOPMENT_OPTIONS} + filterType="developmentStatus" + changeHandler={onAdvancedFilter} + let:id + let:label + > + <label + slot="label" + class="search__checkbox-label" + for={`developmentStatus${id}`} + > + {label} + </label> + </FilterGroup> + <FilterGroup + filterTitle={Drupal.t('Maintenance Status')} + filterData={MAINTENANCE_OPTIONS} + filterType="maintenanceStatus" + changeHandler={onAdvancedFilter} + let:id + let:label + > + <label + slot="label" + class="search__checkbox-label" + for={`maintenanceStatus${id}`} + > + {label} + </label> + </FilterGroup> + <FilterGroup + filterTitle={Drupal.t('Security Advisory Coverage')} + filterData={SECURITY_OPTIONS} + filterType="securityCoverage" + changeHandler={onAdvancedFilter} + let:id + let:label + > + <label + slot="label" + class="search__checkbox-label" + for={`securityCoverage${id}`} + > + {label} + {#if id === COVERED_ID} + <span class="small-icons"> + <ProjectIcon type="status" /> + </span> + {/if} + </label> + </FilterGroup> + </div> +{/if} + +<style> + @media only screen and (min-width: 1200px) { + .small-icons { + margin-top: -3px; + margin-inline-end: 1px; + } + .search__checkbox-label { + font-weight: normal; + padding-inline-start: 35px; + display: flex; + } + .search__checkbox-label { + font-weight: normal; + padding-inline-start: 35px; + display: flex; + margin-right: unset; + } + } +</style> diff --git a/sveltejs/src/Search/SearchSort.svelte b/sveltejs/src/Search/SearchSort.svelte new file mode 100644 index 0000000000000000000000000000000000000000..2c66f18e0a5f2d4e22354dd64c2153ce5ce60d5a --- /dev/null +++ b/sveltejs/src/Search/SearchSort.svelte @@ -0,0 +1,72 @@ +<script> + import { createEventDispatcher, getContext } from 'svelte'; + import { sort, sortCriteria } from '../stores'; + + export let sortText; + export let refresh; + + const { Drupal } = window; + const dispatch = createEventDispatcher(); + const stateContext = getContext('state'); + + async function onSort(event) { + const state = stateContext.getState(); + const detail = { + originalEvent: event, + page: state.page, + pageIndex: state.pageIndex, + pageSize: state.pageSize, + rows: state.filteredRows, + sort: $sort, + }; + + dispatch('sort', detail); + stateContext.setPage(0, 0); + stateContext.setRows(detail.rows); + sortText = $sortCriteria.find((option) => option.id === $sort).text; + refresh(); + } +</script> + +<div class="search__sort"> + <label for="pb-sort">{Drupal.t('Sort by:')}</label> + <select + name="pb-sort" + id="pb-sort" + bind:value={$sort} + on:change={onSort} + class="search__sort-select form-select form-element form-element--type-select" + > + {#each $sortCriteria as opt} + <option value={opt.id}> + {opt.text} + </option> + {/each} + </select> +</div> + +<style> + .search__sort { + z-index: 2; + } + + .search__sort-select { + border: none; + background-color: #d3d4d9; + } + .search__sort-select:hover { + cursor: pointer; + } + + @media screen and (max-width: 855px) { + .search__sort { + margin-bottom: 10px; + margin-top: 10px; + } + } + @media (forced-colors: active) { + #pb-sort { + border: 1px solid; + } + } +</style> diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php index 94bfd583bb13999e0ef4f03f31a8c0706e384d14..fce95669a9aaff9b2cc3c94cef1a55620355d17d 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php @@ -60,7 +60,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); - $cream_cheese_module_selector = '#project-browser .box-2 ul > li:nth-child(1)'; + $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)'; $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button"); $this->assertNotEmpty($download_button); $this->assertSame('Add and Install Cream cheese on a bagel', $download_button->getText()); @@ -83,7 +83,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('text', 'Pinky and the Brain'); - $pinky_brain_selector = '#project-browser .box-2 ul > li:nth-child(2)'; + $pinky_brain_selector = '#project-browser .project-browser__main ul > li:nth-child(2)'; $action_button = $assert_session->waitForElementVisible('css', "$pinky_brain_selector button"); $this->assertNotEmpty($action_button); $this->assertSame('Install Pinky and the Brain', $action_button->getText()); @@ -114,7 +114,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('text', 'Pinky and the Brain'); - $cream_cheese_module_selector = '#project-browser .box-2 ul > li:nth-child(1)'; + $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)'; $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button"); $this->assertNotEmpty($download_button); $this->assertSame('Add and Install Cream cheese on a bagel', $download_button->getText()); @@ -147,7 +147,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install while one is in progress, but not yet in // the applying stage. - $cream_cheese_module_selector = '#project-browser .box-2 ul > li:nth-child(1)'; + $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)'; $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button"); $cream_cheese_button->click(); @@ -182,7 +182,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install while one is in progress, but not yet in // the applying stage. - $cream_cheese_module_selector = '#project-browser .box-2 ul > li:nth-child(1)'; + $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)'; $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button"); $cream_cheese_button->click(); $this->assertTrue($assert_session->waitForText('The install staging area was locked less than 1 minutes ago. This is recent enough that a legitimate installation may be in progress. Consider waiting before unlocking the installation staging area.')); diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php index 87bdb039701fe4fe05d94f3e591e8d95b916a4d0..cafb2c6b63921b90fbe4b687e5b88e7f21bd7c05 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php @@ -227,10 +227,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->svelteInitHelper('text', 'Helvetica'); $page->clickLink('Helvetica'); $this->assertTrue($assert_session->waitForText('Categories:')); - $link = $page->find('css', '.module-page__wrapper__box-2--project-description a'); + $link = $page->find('css', '.module-page__project-description a'); $target = $link->getAttribute('target'); $this->assertEquals('_blank', $target); - } /** @@ -373,7 +372,6 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->svelteInitHelper('text', 'Astronaut Simulator'); $this->pressWithWait('Clear filters'); $this->pressWithWait('Recommended filters'); - $this->assertProjectsVisible([ 'Cream cheese on a bagel', 'Pinky and the Brain', @@ -797,7 +795,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $result_count_text = $page->find('css', '.search__results-count #output')->getText(); $this->assertNotEquals('9 Results Sorted by Active installs', $result_count_text); // Apply the second module category filter. - $second_category_filter_selector = '#project-browser > div.container-1 > div.box-1 > div > form > section > details > fieldset > label:nth-child(3)'; + $second_category_filter_selector = '#project-browser > div.project-browser__container > .project-browser__aside > div > form > section > details > fieldset > label:nth-child(3)'; $this->clickWithWait("$second_category_filter_selector"); // Save the filter applied in second tab. @@ -940,13 +938,13 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->svelteInitHelper('css', $selector['selector']); $this->getSession()->getPage()->pressButton($selector['value']); $this->svelteInitHelper('text', 'Helvetica'); - $assert_session->elementsCount('css', '#project-browser .box-2 ul li:nth-child(7) .categories ul li', 1); - $grid_text = $this->getElementText('#project-browser .box-2 ul li:nth-child(7) .categories ul li:nth-child(1)'); + $assert_session->elementsCount('css', '#project-browser .project-browser__main ul li:nth-child(7) .categories ul li', 1); + $grid_text = $this->getElementText('#project-browser .project-browser__main ul li:nth-child(7) .categories ul li:nth-child(1)'); $this->assertEquals('E-commerce', $grid_text); - $assert_session->elementsCount('css', '#project-browser .box-2 ul li:nth-child(9) .categories ul li', 2); - $grid_text = $this->getElementText('#project-browser .box-2 ul li:nth-child(9) .categories ul li:nth-child(1)'); + $assert_session->elementsCount('css', '#project-browser .project-browser__main ul li:nth-child(9) .categories ul li', 2); + $grid_text = $this->getElementText('#project-browser .project-browser__main ul li:nth-child(9) .categories ul li:nth-child(1)'); $this->assertEquals('Commerce/Advertising', $grid_text); - $grid_text = $this->getElementText('#project-browser .box-2 ul li:nth-child(9) .categories ul li:nth-child(2)'); + $grid_text = $this->getElementText('#project-browser .project-browser__main ul li:nth-child(9) .categories ul li:nth-child(2)'); $this->assertEquals('E-commerce', $grid_text); } } @@ -1001,7 +999,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->inputSearchField('inline form errors'); $this->svelteInitHelper('text', 'Inline Form Errors'); - $install_link = $page->find('css', '.box-2 .action-button__wrapper a'); + $install_link = $page->find('css', '.project-browser__main .action-button__wrapper a'); $this->assertStringContainsString('admin/modules#module-inline-form-errors', $install_link->getAttribute('href')); $this->drupalGet($install_link->getAttribute('href'));