diff --git a/.github/workflows/components.yml b/.github/workflows/components.yml index 3c02114155..61af9c74fb 100644 --- a/.github/workflows/components.yml +++ b/.github/workflows/components.yml @@ -12,11 +12,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 9.0.6 - name: Install Dependencies @@ -37,16 +37,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 with: version: 9.0.6 run_install: true - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' @@ -60,16 +60,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 with: version: 9.0.6 run_install: true - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 11920abb38..32ab756a2e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,14 +11,14 @@ jobs: Docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 9.0.6 run_install: false - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3675ee9a50..e006f5096a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,14 +4,14 @@ jobs: Test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 9.0.6 run_install: false - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' diff --git a/.github/workflows/nuxt.yml b/.github/workflows/nuxt.yml index ff14bf681b..69428ce77f 100644 --- a/.github/workflows/nuxt.yml +++ b/.github/workflows/nuxt.yml @@ -11,14 +11,14 @@ jobs: TestNuxt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 9.0.6 run_install: false - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' @@ -45,10 +45,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 9.0.6 diff --git a/.github/workflows/publish-canary.yml b/.github/workflows/publish-canary.yml index 78d7a87863..c943684eef 100644 --- a/.github/workflows/publish-canary.yml +++ b/.github/workflows/publish-canary.yml @@ -2,8 +2,7 @@ name: Publish Canary on: push: branches: - - develop - - release/alpha-** + - release/* jobs: Canary: runs-on: ubuntu-latest @@ -11,17 +10,17 @@ jobs: packages: write contents: read steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 9.0.6 run_install: false - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: registry-url: 'https://npm.pkg.github.com/' node-version: 20 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0d070a48c5..165faaed6a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,14 +10,14 @@ jobs: packages: write contents: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 9.0.6 run_install: false - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: registry-url: 'https://npm.pkg.github.com/' node-version: 20 diff --git a/.github/workflows/studio.yml b/.github/workflows/studio.yml index 841369eaca..9df644ed6b 100644 --- a/.github/workflows/studio.yml +++ b/.github/workflows/studio.yml @@ -51,7 +51,7 @@ jobs: with: version: 9.0.6 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: version: ${{ matrix.node }} cache: ${{ steps.pkgman.outputs.cache }} diff --git a/.github/workflows/vue.yml b/.github/workflows/vue.yml index 5da53ea05b..7cd8d2f0e8 100644 --- a/.github/workflows/vue.yml +++ b/.github/workflows/vue.yml @@ -11,16 +11,16 @@ jobs: Examples: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 9.0.6 run_install: false - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' diff --git a/.github/workflows/webcomponents.yml b/.github/workflows/webcomponents.yml index 34a72a3a78..81164c941b 100644 --- a/.github/workflows/webcomponents.yml +++ b/.github/workflows/webcomponents.yml @@ -11,16 +11,16 @@ jobs: Examples: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 9.0.6 run_install: false - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' diff --git a/CHANGELOG.md b/CHANGELOG.md index ee9463f420..68ca680bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,158 @@ # Changelog +## v2.14.0 + +[compare changes](https://github.com/dpc-sdp/ripple-framework/compare/v2.13.1...v2.14.0) + + +### 🚀 Enhancements + + - **@dpc-sdp/ripple-ui-core:** ✨ feature flag for long menu titles ([b67cab766](https://github.com/dpc-sdp/ripple-framework/commit/b67cab766)) + - **@dpc-sdp/ripple-ui-maps:** Add clustering distance optional prop, default as current ([ed81faa17](https://github.com/dpc-sdp/ripple-framework/commit/ed81faa17)) + - **@dpc-sdp/ripple-ui-maps:** Add map stories to demonstrate clustering distance (nearer/disabled) ([ad297e4e2](https://github.com/dpc-sdp/ripple-framework/commit/ad297e4e2)) + +### 🩹 Fixes + + - **@dpc-sdp/ripple-tide-api:** 🐛 fixed getTaxonomy only getting first 50 items ([70cea0223](https://github.com/dpc-sdp/ripple-framework/commit/70cea0223)) + - **@dpc-sdp/ripple-tide-api:** 👽️ cheerio default export has been removed ([0b832893b](https://github.com/dpc-sdp/ripple-framework/commit/0b832893b)) + +### 🏡 Chore + + - **docs:** Update modules and links ([7bf5bd6c8](https://github.com/dpc-sdp/ripple-framework/commit/7bf5bd6c8)) + - **docs:** Update NPM reference ([19ed68981](https://github.com/dpc-sdp/ripple-framework/commit/19ed68981)) + +### ✅ Tests + + - ✅ long menu title feature flag ([9b6a42fed](https://github.com/dpc-sdp/ripple-framework/commit/9b6a42fed)) + +### ❤️ Contributors + +- Jason Smith +- Bryce Gilhome +- Jeffrey Dowdle +- David Featherston + +## v2.13.1 + +[compare changes](https://github.com/dpc-sdp/ripple-framework/compare/v2.13.0...v2.13.1) + + +### 🩹 Fixes + + - **@dpc-sdp/ripple-tide-api:** 🐛 fixed getTaxonomy only getting first 50 items ([f46ed87fb](https://github.com/dpc-sdp/ripple-framework/commit/f46ed87fb)) + +### ❤️ Contributors + +- Jeffrey Dowdle + +## v2.13.0 + +[compare changes](https://github.com/dpc-sdp/ripple-framework/compare/v2.12.0...v2.13.0) + + +### 🚀 Enhancements + + - **@dpc-sdp/nuxt-ripple:** Add custom content rating text ([bdce60ca3](https://github.com/dpc-sdp/ripple-framework/commit/bdce60ca3)) + - **@dpc-sdp/ripple-ui-core:** Make sure all table content is rtl ([7cc2831e4](https://github.com/dpc-sdp/ripple-framework/commit/7cc2831e4)) + - **@dpc-sdp/nuxt-ripple:** Add fallback for legacy tide versions ([28d0cdce5](https://github.com/dpc-sdp/ripple-framework/commit/28d0cdce5)) + - **@dpc-sdp/ripple-ui-core:** Support cell alignment ([63766f5cd](https://github.com/dpc-sdp/ripple-framework/commit/63766f5cd)) + - ✨ create sdp-core content type pkg ([bb0a519a9](https://github.com/dpc-sdp/ripple-framework/commit/bb0a519a9)) + - **@dpc-sdp/ripple-tide-search:** Add siteSearchContentTypes featureFlag ([1d2ab6f79](https://github.com/dpc-sdp/ripple-framework/commit/1d2ab6f79)) + - **@dpc-sdp/ripple-tide-search:** Allow individual content types to be toggled on or off ([4cb50a565](https://github.com/dpc-sdp/ripple-framework/commit/4cb50a565)) + - **@dpc-sdp/ripple-tide-search:** Allow address search function to be customised via JSON ([695b18cd1](https://github.com/dpc-sdp/ripple-framework/commit/695b18cd1)) + +### 🩹 Fixes + + - **@dpc-sdp/ripple-tide-search:** Add field_redirect_website check ([e6418d1c2](https://github.com/dpc-sdp/ripple-framework/commit/e6418d1c2)) + - **@dpc-sdp/nuxt-ripple-analytics:** Add fallback origin in dataLayer for relative URLs ([76ee3d1de](https://github.com/dpc-sdp/ripple-framework/commit/76ee3d1de)) + - **@dpc-sdp/ripple-tide-search:** Clear geolocation button errors on search or reset ([15e916abf](https://github.com/dpc-sdp/ripple-framework/commit/15e916abf)) + +### 🏡 Chore + + - **@dpc-sdp/ripple-ui-core:** Cleanup some console/linting warnings ([eb9311e4c](https://github.com/dpc-sdp/ripple-framework/commit/eb9311e4c)) + - Update canary action to publish on release branches ([5aa2fec52](https://github.com/dpc-sdp/ripple-framework/commit/5aa2fec52)) + - Remove develop branch from canary publish ([5542fe498](https://github.com/dpc-sdp/ripple-framework/commit/5542fe498)) + +### ✅ Tests + + - **nuxt-app:** Adding tests for analytics events ([33a499424](https://github.com/dpc-sdp/ripple-framework/commit/33a499424)) + - **nuxt-app:** Adding test for field_redirect_website support ([badd972b2](https://github.com/dpc-sdp/ripple-framework/commit/badd972b2)) + +### ❤️ Contributors + +- Jeffrey Dowdle +- Dylankelly +- David Featherston +- Jason Smith + +## v2.12.0 + +[compare changes](https://github.com/dpc-sdp/ripple-framework/compare/v2.11.1...v2.12.0) + + +### 🚀 Enhancements + + - **@dpc-sdp/ripple-tide-search:** Adds address search and refactors search suggestions ([46609e3a0](https://github.com/dpc-sdp/ripple-framework/commit/46609e3a0)) + - ✨ extend formkit input library ([f93e47d7e](https://github.com/dpc-sdp/ripple-framework/commit/f93e47d7e)) + - **@dpc-sdp/nuxt-ripple-cli:** Update action versions, respect GitHubs prerelease checkbox ([7c91a1dfd](https://github.com/dpc-sdp/ripple-framework/commit/7c91a1dfd)) + - **@dpc-sdp/ripple-ui-maps:** Default map pin color to primary site color instead of red ([6c21be8a1](https://github.com/dpc-sdp/ripple-framework/commit/6c21be8a1)) + - **@dpc-sdp/ripple-tide-search:** Added helper components for laying out result tables ([87a470be6](https://github.com/dpc-sdp/ripple-framework/commit/87a470be6)) + - **@dpc-sdp/ripple-ui-core:** Use classes for list types, remove circle ([3627fb8b1](https://github.com/dpc-sdp/ripple-framework/commit/3627fb8b1)) + - **@dpc-sdp/ripple-ui-core:** Update component tests ([07829674c](https://github.com/dpc-sdp/ripple-framework/commit/07829674c)) + - **@dpc-sdp/ripple-tide-api:** Update list plugin ([e86005d53](https://github.com/dpc-sdp/ripple-framework/commit/e86005d53)) + - **@dpc-sdp/ripple-ui-core:** Add file example to search results ([4a069a7fe](https://github.com/dpc-sdp/ripple-framework/commit/4a069a7fe)) + - **@dpc-sdp/ripple-tide-api:** Use relative URLs for media files ([48199d668](https://github.com/dpc-sdp/ripple-framework/commit/48199d668)) + - **@dpc-sdp/ripple-tide-search:** Allow specifying the url text key ([fed98ed31](https://github.com/dpc-sdp/ripple-framework/commit/fed98ed31)) + +### 🩹 Fixes + + - **@dpc-sdp/ripple-ui-core:** Fixed description list missing types ([db90aaceb](https://github.com/dpc-sdp/ripple-framework/commit/db90aaceb)) + - **@dpc-sdp/ripple-tide-search:** Pass map deadspace to map results hook ([ed11a1468](https://github.com/dpc-sdp/ripple-framework/commit/ed11a1468)) + - **@dpc-sdp/ripple-ui-maps:** Fixed centreMap util when it's given a custom zoom level ([ef6a21741](https://github.com/dpc-sdp/ripple-framework/commit/ef6a21741)) + - **@dpc-sdp/nuxt-ripple:** Fallback to value if no URL exists ([b653e76c3](https://github.com/dpc-sdp/ripple-framework/commit/b653e76c3)) + - **@dpc-sdp/ripple-tide-search:** Add variant prop to locationQueryConfig component ([717ca9985](https://github.com/dpc-sdp/ripple-framework/commit/717ca9985)) + +### 💅 Refactors + + - ♻️ move layer to nuxt-app ([fbffa9ac9](https://github.com/dpc-sdp/ripple-framework/commit/fbffa9ac9)) + +### 📦 Build + + - 👷 update actions to node 20 ([b51d24e9c](https://github.com/dpc-sdp/ripple-framework/commit/b51d24e9c)) + - 👷 update actions to node 20 ([54b46e710](https://github.com/dpc-sdp/ripple-framework/commit/54b46e710)) + +### 🏡 Chore + + - **@dpc-sdp/ripple-tide-search:** Depricate old suggestions props ([9131e3a63](https://github.com/dpc-sdp/ripple-framework/commit/9131e3a63)) + - **ripple-ui-forms-ext:** ⚗️ example layer for custom input ([03f5c3c60](https://github.com/dpc-sdp/ripple-framework/commit/03f5c3c60)) + - Remove unneeded import ([dbc71a6fd](https://github.com/dpc-sdp/ripple-framework/commit/dbc71a6fd)) + - **@dpc-sdp/nuxt-ripple:** Update label ([5d9dd42c6](https://github.com/dpc-sdp/ripple-framework/commit/5d9dd42c6)) + +### ✅ Tests + + - **@dpc-sdp/ripple-tide-search:** Added tests for location suggestions ([b6f1be8a4](https://github.com/dpc-sdp/ripple-framework/commit/b6f1be8a4)) + - **@dpc-sdp/ripple-tide-webform:** ✅ fix unit test ([349ac75c4](https://github.com/dpc-sdp/ripple-framework/commit/349ac75c4)) + - ✅ add cypress tests for form input extend ([4bdb5a5c4](https://github.com/dpc-sdp/ripple-framework/commit/4bdb5a5c4)) + +### ❤️ Contributors + +- David Featherston +- Jeffrey Dowdle +- Jason Smith + +## v2.11.1 + +[compare changes](https://github.com/dpc-sdp/ripple-framework/compare/v2.11.0...v2.11.1) + + +### 🩹 Fixes + + - **@dpc-sdp/ripple-tide-news:** 🐛 added flag option to remove published time ([50c04d3e1](https://github.com/dpc-sdp/ripple-framework/commit/50c04d3e1)) + +### ❤️ Contributors + +- Dylan Kelly + ## v2.11.0 [compare changes](https://github.com/dpc-sdp/ripple-framework/compare/v2.10.3...v2.11.0) diff --git a/docs/content/framework/4.core-modules.md b/docs/content/framework/4.core-modules.md index 292a9c1411..dbcccdbf8e 100644 --- a/docs/content/framework/4.core-modules.md +++ b/docs/content/framework/4.core-modules.md @@ -4,37 +4,48 @@ description: A listing of all current core Ripple layers and packages layout: page --- -View each package on NPM for their individual READMEs. +View each package on GitHub package registry for their individual READMEs. ## Core UI Libraries -- [`ripple-ui-core`](https://www.npmjs.com/package/@dpc-sdp/ripple-ui-core) -- [`ripple-ui-forms`](https://www.npmjs.com/package/@dpc-sdp/ripple-ui-forms) +- [`ripple-ui-core`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-ui-core) +- [`ripple-ui-forms`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-ui-forms) +- [`ripple-ui-maps`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-ui-maps) ## Tide API -- [`ripple-tide-api`](https://www.npmjs.com/package/@dpc-sdp/ripple-tide-api) +- [`ripple-tide-api`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-api) ## Nuxt-ripple base layer -- [`nuxt-ripple`](https://www.npmjs.com/package/@dpc-sdp/nuxt-ripple) +- [`nuxt-ripple`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/nuxt-ripple) ## Core content types -- [`ripple-tide-landing-page`](https://www.npmjs.com/package/@dpc-sdp/ripple-tide-landing-page) -- [`ripple-tide-event`](https://www.npmjs.com/package/@dpc-sdp/ripple-tide-event) -- [`ripple-tide-grant`](https://www.npmjs.com/package/@dpc-sdp/ripple-tide-grant) -- [`ripple-tide-media`](https://www.npmjs.com/package/@dpc-sdp/ripple-tide-media) -- [`ripple-tide-news`](https://www.npmjs.com/package/@dpc-sdp/ripple-tide-news) -- [`ripple-tide-publication`](https://www.npmjs.com/package/@dpc-sdp/ripple-tide-publication) +All core content types (and content related packages) are added through the `ripple-sdp-core` package + +- [`ripple-sdp-core`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-sdp-core) + +Below are the individual content type packages that are included in `ripple-sdp-core`: + +- [`ripple-tide-search`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-search) +- [`ripple-tide-webform`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-webform) +- [`ripple-tide-topic`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-topic) +- [`ripple-tide-landing-page`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-landing-page) +- [`ripple-tide-event`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-event) +- [`ripple-tide-grant`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-grant) +- [`ripple-tide-media`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-media) +- [`ripple-tide-news`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-news) +- [`ripple-tide-publication`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-tide-publication) ## Other core modules -- [`ripple-tide-search`](https://www.npmjs.com/package/@dpc-sdp/ripple-tide-search) -- [`nuxt-ripple-analytics`](https://www.npmjs.com/package/@dpc-sdp/nuxt-ripple-analytics) -- [`nuxt-ripple-preview`](https://www.npmjs.com/package/@dpc-sdp/nuxt-ripple-preview) +- [`nuxt-ripple-analytics`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/nuxt-ripple-analytics) +- [`nuxt-ripple-preview`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/nuxt-ripple-preview) ## Scaffolding, testing and supporting packages -- [`nuxt-ripple-cli`](https://www.npmjs.com/package/@dpc-sdp/nuxt-ripple-cli) -- [`ripple-test-utils`](https://www.npmjs.com/package/@dpc-sdp/ripple-test-utils) +- [`nuxt-ripple-cli`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/nuxt-ripple-cli) +- [`ripple-test-utils`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/ripple-test-utils) +- [`eslint-config-ripple`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/eslint-config-ripple) +- [`stylelint-config-ripple`](https://github.com/dpc-sdp/ripple-framework/pkgs/npm/stylelint-config-ripple) diff --git a/examples/nuxt-app/app.config.ts b/examples/nuxt-app/app.config.ts index 5057aacc17..e5344eb6a0 100644 --- a/examples/nuxt-app/app.config.ts +++ b/examples/nuxt-app/app.config.ts @@ -35,6 +35,27 @@ export default defineAppConfig({ // `dynamicValue` is used in a cypress test to ensure fallbackValues function are called dynamicValue: () => ['blue'] }, + suggestionsFunctions: { + exampleSuggestionsFn: async (query, args) => { + return [ + { + id: '1', + name: `Test location - ${args.testArg} - 1`, + center: [144.9631, -37.8136] + }, + { + id: '2', + name: `Test location - ${args.testArg} - 2`, + center: [-33.8688, 151.2093] + }, + { + id: '2', + name: `With magic key`, + arcGISMagicKey: 'fake1234' + } + ] + } + }, filterFunctions: { // `dummyFunctionFilter` is used in a cypress test to check that the correct parameters are passed to custom filter functions dummyFunctionFilter: (filterConfig, values) => { diff --git a/examples/nuxt-app/cypress.config.mjs b/examples/nuxt-app/cypress.config.mjs index 27edbe8967..96355fdc5e 100644 --- a/examples/nuxt-app/cypress.config.mjs +++ b/examples/nuxt-app/cypress.config.mjs @@ -12,7 +12,6 @@ export default defineConfig({ baseUrl: 'http://localhost:3000', specPattern: '**/*.{feature,feature.ts}', supportFile: false, - supportFolder: './test', downloadsFolder: './test/downloads', fixturesFolder: './test/fixtures', videosFolder: './test/videos', diff --git a/examples/nuxt-app/layers/ripple-ui-forms-ext/__test__/fixtures/webform.json b/examples/nuxt-app/layers/ripple-ui-forms-ext/__test__/fixtures/webform.json new file mode 100644 index 0000000000..8218186cf1 --- /dev/null +++ b/examples/nuxt-app/layers/ripple-ui-forms-ext/__test__/fixtures/webform.json @@ -0,0 +1,155 @@ +{ + "title": "Webform", + "changed": "2024-06-07T17:15:09+10:00", + "created": "2024-05-02T14:42:13+10:00", + "type": "landing_page", + "nid": "45fa8751-74af-43ee-9cb9-8a8fdee7e593", + "_sectionId": "8888", + "sidebar": { + "contacts": [], + "relatedLinks": [], + "whatsNext": [], + "socialShareNetworks": ["Facebook", "X", "LinkedIn"], + "siteSectionNav": null + }, + "status": "published", + "topicTags": [ + { + "text": "Demo Topic", + "url": "/topic/demo-topic" + } + ], + "siteSection": { + "id": 8888, + "name": "Demo Site", + "siteOverrides": { + "showQuickExit": null, + "theme": {}, + "featureFlags": {} + } + }, + "meta": { + "url": "/webform", + "langcode": "en", + "description": "Testing the landing page webform mapping", + "additional": [ + { + "tag": "meta", + "attributes": { + "name": "title", + "content": "Webform | Single Digital Presence Content Management System" + } + }, + { + "tag": "link", + "attributes": { + "rel": "canonical", + "href": "http://content-sdp.docker.internal/webform" + } + }, + { + "tag": "meta", + "attributes": { + "property": "og:locale", + "content": "en-AU" + } + } + ], + "keywords": "", + "image": null + }, + "showContentRating": true, + "summary": "Testing the landing page webform mapping", + "showHeroAcknowledgement": false, + "showInPageNav": false, + "showHeroImageCaption": false, + "showTopicTags": true, + "inPageNavHeadingLevel": "h2", + "background": "default", + "header": { + "title": "Webform", + "summary": "", + "links": { + "title": "", + "items": [], + "more": null + }, + "backgroundImageCaption": "", + "theme": "default", + "logoImage": null, + "backgroundImage": null, + "cornerTop": null, + "cornerBottom": null, + "primaryAction": null, + "secondaryAction": null, + "secondaryActionLabel": "" + }, + "primaryCampaign": null, + "secondaryCampaign": null, + "headerComponents": [], + "bodyComponents": [ + { + "uuid": "986aca36-9a3a-408c-9455-c00396dcfb39", + "component": "TideLandingPageWebForm", + "id": 1795, + "title": "Input support", + "props": { + "title": "Input support", + "formId": "test_of_fields", + "hideFormOnSubmit": false, + "successMessageTitle": "Form submitted", + "successMessageHTML": "Thank you! Your response has been submitted.", + "errorMessageTitle": "Form not submitted", + "errorMessageHTML": "We are experiencing a server error. Please try again, otherwise contact us.", + "schema": [ + { + "$formkit": "RplFormText", + "key": "name", + "name": "name", + "label": "Supported", + "id": "test_of_fields_name", + "validation": [["length", 0, 255]], + "validationMessages": { + "required": "Name is required", + "accepted": "Name is required", + "length": "You can enter a maximum of 255 characters" + } + }, + { + "$formkit": "RplFormItem", + "key": "test", + "name": "test", + "label": "Extended", + "id": "test_of_fields_test", + "validation": [["length", 0, 255]], + "validationMessages": { + "required": "Item is required!", + "accepted": "Item is required", + "length": "You can enter a maximum of 255 characters" + } + }, + { + "$el": "div", + "attrs": { + "class": "rpl-form__outer rpl-form__input--unsupported" + }, + "children": ["\"signature\" is not yet supported"] + }, + { + "$formkit": "RplFormActions", + "key": "actions", + "name": "submit", + "variant": "filled", + "id": "test_of_fields_actions", + "displayResetButton": false, + "validation": [], + "validationMessages": { + "required": "Submit is required", + "accepted": "Submit is required" + } + } + ] + } + } + ] +} diff --git a/examples/nuxt-app/layers/ripple-ui-forms-ext/__test__/forms-ext.feature b/examples/nuxt-app/layers/ripple-ui-forms-ext/__test__/forms-ext.feature new file mode 100644 index 0000000000..810148551e --- /dev/null +++ b/examples/nuxt-app/layers/ripple-ui-forms-ext/__test__/forms-ext.feature @@ -0,0 +1,20 @@ +Feature: Forms, extended + + As a developer, I can add custom form inputs that are not provided in the core offering + + Background: + Given the mock server has started + And the page endpoint for path "/webform" returns fixture "../../layers/ripple-ui-forms-ext/__test__/fixtures/webform" with status 200 + And the site endpoint returns fixture "/site/reference" with status 200 + + @mockserver + Scenario: Custom input + Given I visit the page "/webform" + Then the landing page component "TideLandingPageWebForm" should exist + And a field labelled "Extended" should exist with the CSS class ".rpl-form__input--type-item" + + @mockserver + Scenario: Unsupported input + Given I visit the page "/webform" + Then the landing page component "TideLandingPageWebForm" should exist + And an input of type "signature" is not yet supported diff --git a/examples/nuxt-app/layers/ripple-ui-forms-ext/app.config.ts b/examples/nuxt-app/layers/ripple-ui-forms-ext/app.config.ts new file mode 100644 index 0000000000..a596cbeb1f --- /dev/null +++ b/examples/nuxt-app/layers/ripple-ui-forms-ext/app.config.ts @@ -0,0 +1,20 @@ +import { defineAppConfig } from '#imports' +import itemMapping from './mapping/item' + +export default defineAppConfig({ + ripple: { + customInputs: { + item: { + id: 'RplFormItem', + type: 'item', + formkitDefProps: { + type: 'input', + family: 'text', + props: ['placeholder', 'validationMeta', 'columnClasses', 'pii'], + forceTypeProp: 'text' + }, + mapping: itemMapping + } + } + } +}) diff --git a/examples/nuxt-app/layers/ripple-ui-forms-ext/components/global/RplFormItem.vue b/examples/nuxt-app/layers/ripple-ui-forms-ext/components/global/RplFormItem.vue new file mode 100644 index 0000000000..3872e99a25 --- /dev/null +++ b/examples/nuxt-app/layers/ripple-ui-forms-ext/components/global/RplFormItem.vue @@ -0,0 +1,165 @@ + + + + + + + diff --git a/examples/nuxt-app/layers/ripple-ui-forms-ext/mapping/item.ts b/examples/nuxt-app/layers/ripple-ui-forms-ext/mapping/item.ts new file mode 100644 index 0000000000..9ac2320fb4 --- /dev/null +++ b/examples/nuxt-app/layers/ripple-ui-forms-ext/mapping/item.ts @@ -0,0 +1,30 @@ +import type { TideWebformElement } from '@dpc-sdp/ripple-tide-webform/types' +import { + getValidationAndConditionals, + getInputIcons +} from '@dpc-sdp/ripple-tide-webform/mapping/utils' + +const logger = { + warn: (message: string, props: { label: string }) => { + console.warn(props.label, message) + } +} + +// Tide API mapping function +export default ( + fieldID: string, + field: TideWebformElement, + fieldKey: string +) => ({ + $formkit: 'RplFormItem', + key: fieldKey, + name: fieldKey, + label: field['#title'], + disabled: field['#disabled'], + placeholder: field['#placeholder'], + id: fieldID, + help: field['#description'] || field['#help_title'], + value: field['#default_value'], + ...getValidationAndConditionals(field, logger), + ...getInputIcons(field) +}) diff --git a/examples/nuxt-app/layers/ripple-ui-forms-ext/nuxt.config.ts b/examples/nuxt-app/layers/ripple-ui-forms-ext/nuxt.config.ts new file mode 100644 index 0000000000..3a774af39d --- /dev/null +++ b/examples/nuxt-app/layers/ripple-ui-forms-ext/nuxt.config.ts @@ -0,0 +1,9 @@ +import { defineNuxtConfig } from 'nuxt/config' + +export default defineNuxtConfig({ + hooks: { + 'build:done': async () => { + console.info('Added ripple-ui-forms-ext components') + } + } +}) diff --git a/examples/nuxt-app/layers/ripple-ui-forms-ext/package.json b/examples/nuxt-app/layers/ripple-ui-forms-ext/package.json new file mode 100644 index 0000000000..66243bea10 --- /dev/null +++ b/examples/nuxt-app/layers/ripple-ui-forms-ext/package.json @@ -0,0 +1,16 @@ +{ + "name": "@dpc-sdp/ripple-ui-forms-ext", + "description": "Custom form inputs", + "version": "2.10.1", + "license": "Apache-2.0", + "repository": "https://github.com/dpc-sdp/ripple-framework", + "main": "./nuxt.config.ts", + "type": "module", + "exports": { + ".": "./nuxt.config.ts" + }, + "dependencies": { + "@dpc-sdp/ripple-tide-webform": "workspace:*", + "@dpc-sdp/ripple-ui-forms": "workspace:*" + } +} diff --git a/examples/nuxt-app/layers/ripple-ui-forms-ext/pages/_test/webform.vue b/examples/nuxt-app/layers/ripple-ui-forms-ext/pages/_test/webform.vue new file mode 100644 index 0000000000..f50c50471d --- /dev/null +++ b/examples/nuxt-app/layers/ripple-ui-forms-ext/pages/_test/webform.vue @@ -0,0 +1,12 @@ + + + diff --git a/examples/nuxt-app/layers/ripple-ui-forms-ext/tsconfig.json b/examples/nuxt-app/layers/ripple-ui-forms-ext/tsconfig.json new file mode 100644 index 0000000000..99fbedd0b0 --- /dev/null +++ b/examples/nuxt-app/layers/ripple-ui-forms-ext/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./../../.nuxt/tsconfig.json" +} diff --git a/examples/nuxt-app/nuxt.config.ts b/examples/nuxt-app/nuxt.config.ts index 16c703c7e2..1e31a21e82 100644 --- a/examples/nuxt-app/nuxt.config.ts +++ b/examples/nuxt-app/nuxt.config.ts @@ -19,15 +19,8 @@ export default defineNuxtConfig({ '@dpc-sdp/nuxt-ripple', '@dpc-sdp/nuxt-ripple-analytics', '@dpc-sdp/nuxt-ripple-preview', - '@dpc-sdp/ripple-tide-event', - '@dpc-sdp/ripple-tide-topic', - '@dpc-sdp/ripple-tide-landing-page', - '@dpc-sdp/ripple-tide-grant', - '@dpc-sdp/ripple-tide-publication', - '@dpc-sdp/ripple-tide-media', - '@dpc-sdp/ripple-tide-news', - '@dpc-sdp/ripple-tide-search', - '@dpc-sdp/ripple-tide-webform' + '@dpc-sdp/ripple-sdp-core', + './layers/ripple-ui-forms-ext' ], // Nuxt devtools sourcemap: true, diff --git a/examples/nuxt-app/package.json b/examples/nuxt-app/package.json index e82065a2ba..81a1ec4fd1 100644 --- a/examples/nuxt-app/package.json +++ b/examples/nuxt-app/package.json @@ -25,15 +25,7 @@ "@dpc-sdp/nuxt-ripple-analytics": "workspace:*", "@dpc-sdp/nuxt-ripple-cli": "workspace:*", "@dpc-sdp/nuxt-ripple-preview": "workspace:*", - "@dpc-sdp/ripple-tide-event": "workspace:*", - "@dpc-sdp/ripple-tide-grant": "workspace:*", - "@dpc-sdp/ripple-tide-landing-page": "workspace:*", - "@dpc-sdp/ripple-tide-media": "workspace:*", - "@dpc-sdp/ripple-tide-news": "workspace:*", - "@dpc-sdp/ripple-tide-publication": "workspace:*", - "@dpc-sdp/ripple-tide-search": "workspace:*", - "@dpc-sdp/ripple-tide-topic": "workspace:*", - "@dpc-sdp/ripple-tide-webform": "workspace:*", + "@dpc-sdp/ripple-sdp-core": "workspace:*", "@dpc-sdp/ripple-ui-maps": "workspace:*" }, "devDependencies": { diff --git a/examples/nuxt-app/test/features/landingpage/page-components.feature b/examples/nuxt-app/test/features/landingpage/page-components.feature index 355ffaa933..6001bcc557 100644 --- a/examples/nuxt-app/test/features/landingpage/page-components.feature +++ b/examples/nuxt-app/test/features/landingpage/page-components.feature @@ -17,6 +17,12 @@ Feature: Home page | Accordion #1 | Test rich text content #1 | | Accordion #2 | Test rich text content #2 | And the accordion with ID "972" should display the description "Test accordion description" + When I click the accordion item "Accordion #1" in accordion with ID "accordion-972" + When I click the accordion item "Accordion #1" in accordion with ID "accordion-972" + Then the dataLayer should include the following events + | event | element_id | element_text | name | component | + | open_accordion | accordion-972-970 | Accordion #1 | Test accordion title | rpl-accordion | + | close_accordion | accordion-972-970 | Accordion #1 | Test accordion title | rpl-accordion | @mockserver Scenario: Page component - Accordion (Open/close all) @@ -25,6 +31,10 @@ Feature: Home page When I click the close all button on accordion with ID "accordion-972" Then all accordion items in accordion ID "accordion-972" should be hidden + And the dataLayer should include the following events + | event | element_id | element_text | name | component | + | open_accordion_all | accordion-972 | Open all | Test accordion title | rpl-accordion | + | close_accordion_all | accordion-972 | Close all | Test accordion title | rpl-accordion | @mockserver Scenario: Page component - Promo card @@ -54,7 +64,6 @@ Feature: Home page | displayStyle | title | content | image | | featured | Nav card (featured) | Sample description | /placeholders/medium.png | - @mockserver Scenario: Page component - Key dates card Then a key dates card with ID "988" should exist with the title "Key calendar dates" @@ -68,10 +77,10 @@ Feature: Home page Scenario: Page component - Timeline Then a timeline with ID "992" should exist with the title "Test timeline title" Then a timeline with ID "992" should exist with the following items - | title | date | summary | url | image | - | Milestone 1 title | 2 June to 11 November | Milestone 1 summary field | /test-destination-1 | /placeholders/small.png | - | Milestone 2 title | 4 October to 17 November | Milestone 2 summary field | /test-destination-2 | | - | Milestone 3 title | | Milestone 3 text | | | + | title | date | summary | url | image | + | Milestone 1 title | 2 June to 11 November | Milestone 1 summary field | /test-destination-1 | /placeholders/small.png | + | Milestone 2 title | 4 October to 17 November | Milestone 2 summary field | /test-destination-2 | | + | Milestone 3 title | | Milestone 3 text | | | @mockserver Scenario: Page component - Call to action @@ -106,6 +115,11 @@ Feature: Home page Then the active slide on "1155" should be "1" And the pagination button "Previous" on "1155" should be disabled + And the dataLayer should include the following events + | event | element_id | element_text | component | + | paginate_next | page-component-1155 | Next | rpl-card-carousel | + | paginate_prev | page-component-1155 | Previous | rpl-card-carousel | + @mockserver Scenario: Page component - Media Gallery Given a media gallery with ID "1056" should exist with the following gallery items @@ -124,6 +138,10 @@ Feature: Home page And the pagination button "Next" on "1056" should be disabled When I click the button "Previous" on the component with ID "1056" Then the active slide on "1056" should be "2" + And the dataLayer should include the following events + | event | element_id | element_text | label | component | + | paginate_next | page-component-1056 | Next | Media title two | rpl-media-gallery | + | paginate_prev | page-component-1056 | Previous | Media title two | rpl-media-gallery | @mockserver Scenario: Page component - Media Gallery (Modal) @@ -131,6 +149,10 @@ Feature: Home page Then the "media-gallery" modal should be "visible" When I click the "media-gallery" modal button "Close" Then the "media-gallery" modal should be "hidden" + And the dataLayer should include the following events + | event | element_id | element_text | label | component | + | enter_fullscreen | page-component-1056 | View 'Media title one' fullscreen | Media title one | rpl-media-gallery | + | exit_fullscreen | page-component-1056 | Close | Media title one | rpl-media-gallery | @mockserver Scenario: Page component - Data Table @@ -157,3 +179,29 @@ Feature: Home page | title | content | image | url | | Card one | Card one summary | /placeholders/medium.png | /landing-page-cc-2 | | Card two | Card two summary | /placeholders/medium.png | https://google.com/ | + + @mockserver + Scenario: Page component - Complex image/Media Embed + Given a media embed with ID "1951" should exist + Then the media embed image for "1951" should be "/placeholders/medium.png" + When I click the action "More info" for media embed "1951" + Then the extra data for media embed "1951" should be visible + When I click the action "More info" for media embed "1951" + Then the extra data for media embed "1951" should be hidden + + When I click the action "Fullscreen" for media embed "1951" + Then the "media-embed" modal should be "visible" + When I click the "media-embed" modal button "Close" + Then the "media-embed" modal should be "hidden" + + And the dataLayer should include the following events + | event | element_id | element_text | label | component | + | open_data | page-component-1951 | More info | Complex image | rpl-media-embed | + | close_data | page-component-1951 | More info | Complex image | rpl-media-embed | + | enter_fullscreen | page-component-1951 | Fullscreen | Complex image | rpl-media-embed | + | exit_fullscreen | page-component-1951 | Close | Complex image | rpl-media-embed | + + When I trigger a click for the action "Download it" on the media embed "1951" + Then the dataLayer should include the following events + | event | element_id | element_text | label | file_name | file_extension | type | component | + | file_download | page-component-1951 | Download it | Complex image | medium.png | png | image | rpl-media-embed | diff --git a/examples/nuxt-app/test/features/maps/geolocate.feature b/examples/nuxt-app/test/features/maps/geolocate.feature new file mode 100644 index 0000000000..4f0ccb5696 --- /dev/null +++ b/examples/nuxt-app/test/features/maps/geolocate.feature @@ -0,0 +1,34 @@ +Feature: Custom collection map component + + I want to display a map of features from an indexed data pipeline + + Background: + Given the site endpoint returns fixture "/site/vic" with status 200 + And the search autocomplete request is stubbed with "/search-listing/suggestions/none" fixture + Given I am using a "macbook-16" device + Given the "/api/tide/elasticsearch/elasticsearch_index_develop_node/_search" aggregation request is stubbed with fixture "/map-table/vsba/aggregations" and status 200 as alias "aggReq" + Given the "/test-map-shape-layer" network request is stubbed with fixture "/maps/sample-shapes" + | method | status | + | GET | 200 | + Given the "https://base.maps.vic.gov.au/service*" network request is stubbed with fixture "/maps/service.png" + | method | status | + | GET | 200 | + + @mockserver + Scenario: The geolocate button is hidden when not enabled + Given I load the page fixture with "/maps/basic-page" + Given the geolocation button is not enabled + And the page endpoint for path "/map" returns the loaded fixture + And the "/api/tide/elasticsearch/elasticsearch_index_develop_node/_search" network request is delayed by 1000 milliseconds and stubbed with fixture "/site/search-response", status 200 and alias "searchReq" + Given I visit the page "/map" + + Then the geolocate button is hidden + + @mockserver + Scenario: The geolocate button is shown when enabled + Given I load the page fixture with "/maps/basic-page" + Given the geolocation button is enabled + And the page endpoint for path "/map" returns the loaded fixture + And the "/api/tide/elasticsearch/elasticsearch_index_develop_node/_search" network request is delayed by 1000 milliseconds and stubbed with fixture "/site/search-response", status 200 and alias "searchReq" + Given I visit the page "/map" + Then the geolocate button is displayed diff --git a/examples/nuxt-app/test/features/maps/suggestions.feature b/examples/nuxt-app/test/features/maps/suggestions.feature new file mode 100644 index 0000000000..dd503ee9be --- /dev/null +++ b/examples/nuxt-app/test/features/maps/suggestions.feature @@ -0,0 +1,70 @@ +Feature: Suggestions + + I want to be able to customise the suggestions for location search + + Background: + Given the site endpoint returns fixture "/site/vic" with status 200 + And the location autocomplete request is stubbed with "/maps/example-suburbs-response" fixture + Given I am using a "macbook-16" device + Given the "/api/tide/elasticsearch/elasticsearch_index_develop_node/_search" aggregation request is stubbed with fixture "/map-table/vsba/aggregations" and status 200 as alias "aggReq" + Given the "/test-map-shape-layer" network request is stubbed with fixture "/maps/sample-shapes" + | method | status | + | GET | 200 | + Given the "https://base.maps.vic.gov.au/service*" network request is stubbed with fixture "/maps/service.png" + | method | status | + | GET | 200 | + Given the ArcGIS findAddressCandidates endpoint returns "/maps/arcgis-address-candidates" fixture + + @mockserver + Scenario: Default + Given I load the page fixture with "/maps/basic-page" + Given the page endpoint for path "/map" returns the loaded fixture + Given the "/api/tide/elasticsearch/elasticsearch_index_develop_node/_search" network request is stubbed with fixture "/maps/simple-map-results" and status 200 as alias "searchReq" + Given I visit the page "/map?activeTab=listing" + And I wait 2 seconds + And I type "bays" into the location search bar + And the search suggestions displayed should include + | Bayswater North | + | Bayswater | + And I wait 2 seconds + + @mockserver + Scenario: Custom suggestions function + Given I load the page fixture with "/maps/basic-page" + And a custom suggestions function called "exampleSuggestionsFn" is used + Given the page endpoint for path "/map" returns the loaded fixture + Given the "/api/tide/elasticsearch/elasticsearch_index_develop_node/_search" network request is stubbed with fixture "/maps/simple-map-results" and status 200 as alias "searchReq" + Given I visit the page "/map?activeTab=listing" + And I wait 2 seconds + And I type "bays" into the location search bar + And the search suggestions displayed should include + | Test location - testValue - 1 | + | Test location - testValue - 2 | + | With magic key | + And I wait 2 seconds + And I click the search suggestion labelled "Test location - testValue - 1" + Then the URL should reflect that the location has the following: + | key | value | + | id | 1 | + | name | Test location - testValue - 1 | + + @mockserver + Scenario: Custom suggestions function with ArcGIS magic keys + Given I load the page fixture with "/maps/basic-page" + And a custom suggestions function called "exampleSuggestionsFn" is used + Given the page endpoint for path "/map" returns the loaded fixture + Given the "/api/tide/elasticsearch/elasticsearch_index_develop_node/_search" network request is stubbed with fixture "/maps/simple-map-results" and status 200 as alias "searchReq" + Given I visit the page "/map?activeTab=listing" + And I wait 2 seconds + And I type "bays" into the location search bar + And the search suggestions displayed should include + | Test location - testValue - 1 | + | Test location - testValue - 2 | + | With magic key | + And I wait 2 seconds + And I click the search suggestion labelled "With magic key" + Then the URL should reflect that the location has the following: + | key | value | + | id | fake1234 | + | name | 1234 Fake St Fakeville Vic 3000 | + diff --git a/examples/nuxt-app/test/features/publication/publication.feature b/examples/nuxt-app/test/features/publication/publication.feature index d50e93c1b1..1b83d5d2d1 100644 --- a/examples/nuxt-app/test/features/publication/publication.feature +++ b/examples/nuxt-app/test/features/publication/publication.feature @@ -22,6 +22,14 @@ Feature: Publication page | The Victorian Skills Plan 2022 into 2023 actions and initiatives | The 25 initiatives scheduled to start in year one are well underway. | /victorian-skills-plan-2023-implementation-update/2022-victorian-skills-plan-actions-and-initiatives | | Promoting post-secondary education skills and career pathways | The first priority area's actions and initiatives from the Victorian Skills Plan 2022. | /victorian-skills-plan-2023-implementation-update/promoting-post-secondary-education-skills-and-career | | Lifting participation in education and training | The second priority area's actions and initiatives from the Victorian Skills Plan 2022. | /victorian-skills-plan-2023-implementation-update/lifting-participation-education-and-training | + And the publication should display the following documents + | title | url | type | size | + | Victorian Skills Plan Implementation Update October 2023 | /sites/default/files/2023-10/16686-VSA-Implementation-Plan-Section_FA_Digital.pdf | pdf | 4.61 MB | + | Print full document | /victorian-skills-plan-2023-implementation-update/print-all | | | + When I click on the document "Victorian Skills Plan Implementation Update October 2023" + Then the dataLayer should include the following events + | event | element_text | file_name | file_extension | file_size | component | + | file_download | Victorian Skills Plan Implementation Update October 2023 | 16686-VSA-Implementation-Plan-Section_FA_Digital.pdf | pdf | 4.61 MB | rpl-file | @mockserver Example: Publication child @@ -29,6 +37,10 @@ Feature: Publication page Then the title should be "The Victorian Skills Plan 2022 into 2023 actions and initiatives" And there should be a page link with a title of "Previous" and description text of "Victorian Skills Plan Implementation Update" And there should be a page link with a title of "Next" and description text of "Promoting post-secondary education skills and career pathways" + And the publication should display the following documents + | title | url | type | size | + | Victorian Skills Plan Implementation Update October 2023 | /sites/default/files/2023-10/16686-VSA-Implementation-Plan-Section_FA_Digital.pdf | pdf | 4.61 MB | + | Print full document | /victorian-skills-plan-2023-implementation-update/print-all | | | @mockserver Example: Publication print all diff --git a/examples/nuxt-app/test/features/search-listing/list.feature b/examples/nuxt-app/test/features/search-listing/list.feature index 6a40b233c2..5a4026b44d 100644 --- a/examples/nuxt-app/test/features/search-listing/list.feature +++ b/examples/nuxt-app/test/features/search-listing/list.feature @@ -20,10 +20,10 @@ Feature: List layout And the search network request should be called with the "/search-listing/list/request" fixture And the search listing results count should read "Displaying 1-9 of 124 results" And the search listing results should have following items: - | title | content | component | - | 2-BE-event-1 | The ingenious hero who travelled far and wide | tide-search-result | - | 5-BE-land-3 Landing Page | Outside thundered the approaching surf of war | tide-search-result | - | Accessibility - demo | Accessibility information about this website. | tide-search-result | + | title | content | url | component | + | 2-BE-event-1 | The ingenious hero who travelled far and wide | /2-be-event-1 | tide-search-result | + | 5-BE-land-3 Landing Page | Outside thundered the approaching surf of war | /5-be-land-3-landing-page-complete-test | tide-search-result | + | Accessibility - demo | Accessibility information about this website. | https://vic.gov.au/page | tide-search-result | @mockserver Example: A custom skeleton loader can be use for list result items diff --git a/examples/nuxt-app/test/features/search-listing/table.feature b/examples/nuxt-app/test/features/search-listing/table.feature index d338bbf87a..0cb8c0a8c5 100644 --- a/examples/nuxt-app/test/features/search-listing/table.feature +++ b/examples/nuxt-app/test/features/search-listing/table.feature @@ -59,7 +59,7 @@ Feature: Table layout Then the tables extra content should contain the label "Funded for" and text "Multicultural Service" And the tables extra content should contain the label "Email", value "contact@africanfamilyservices.org.au" and link "mailto:contact@africanfamilyservices.org.au" And the tables extra content should contain the label "Phone", value "03 9602 5046" and link "tel:03 9602 5046" - And the tables extra content should contain the label "Website", value "http://africanfamilyservices.org.au/#contact-us" and link "http://africanfamilyservices.org.au/#contact-us" + And the tables extra content should contain the label "Website", value "African Family Services" and link "http://africanfamilyservices.org.au/#contact-us" And the tables extra content should contain the text "Includes statewide service" And the tables extra content should contain the class "rpl-tag--dark" And the table row with text "Department with no extra content" should not display more information diff --git a/examples/nuxt-app/test/features/site/analytics.feature b/examples/nuxt-app/test/features/site/analytics.feature new file mode 100644 index 0000000000..694c88aaab --- /dev/null +++ b/examples/nuxt-app/test/features/site/analytics.feature @@ -0,0 +1,14 @@ +Feature: Analytics + + Background: + Given the site endpoint returns fixture "/site/reference" with status 200 + + @mockserver + Scenario: DataLayer events + Given the site endpoint returns fixture "/site/reference" with status 200 + And the page endpoint for path "/" returns fixture "/landingpage/home" with status 200 + Given I visit the page "/" + + Then the dataLayer should include the following events + | event | page_title | page_url | content_type | + | routeChange | Demo Landing Page | / | landing_page | diff --git a/examples/nuxt-app/test/features/site/search.feature b/examples/nuxt-app/test/features/site/search.feature index a860445d10..4a86154c6d 100644 --- a/examples/nuxt-app/test/features/site/search.feature +++ b/examples/nuxt-app/test/features/site/search.feature @@ -1,10 +1,8 @@ Feature: Site search - Background: - Given the site endpoint returns fixture "/site/reference" with status 200 - @mockserver Example: Display and manage site search results + Given the site endpoint returns fixture "/site/reference" with status 200 Given the "/api/tide/search/**" network request is delayed by 500 milliseconds and stubbed with fixture "/site/search-response", status 200 and alias "siteSearchReq" When I visit the page "/search?q=demo" Then the search listing skeleton should display 10 items with the class "tide-search-result-skeleton" @@ -35,8 +33,22 @@ Feature: Site search Then the filters toggle should show 0 applied filters And the search input should have the value "" + @mockserver + Example: Overrides site search content types with feature flag + Given the "/api/tide/search/**" network request is stubbed with fixture "/site/search-response" and status 200 as alias "siteSearchReq" + Then I load the site fixture with "/site/reference" + And the feature flag "search.contentTypes.grant" is set to "false" + And the feature flag "search.contentTypes.product" is set to "true" + And the site endpoint returns the loaded fixture + + When I visit the page "/search?q=demo" + Then I toggle the search listing filters section + And I clear the search filters + Then the network request "siteSearchReq" should be called with the "/site/search-request-content-types" fixture + @mockserver Example: Search bar max input length + Given the site endpoint returns fixture "/site/reference" with status 200 Given the "/api/tide/search/**" network request is stubbed with fixture "/site/search-response" and status 200 as alias "siteSearchReq" When I visit the page "/search" Then the search input should be have a max length of 128 diff --git a/examples/nuxt-app/test/features/site/shared-elements.feature b/examples/nuxt-app/test/features/site/shared-elements.feature index 0f967b88eb..6fea7fa727 100644 --- a/examples/nuxt-app/test/features/site/shared-elements.feature +++ b/examples/nuxt-app/test/features/site/shared-elements.feature @@ -83,6 +83,28 @@ Feature: Shared site elements | Demo Topic | /topic/demo-topic | | Demo Tag | /tags/demo-tag | + @mockserver + Scenario: Content Rating (visible) + Given the site endpoint returns fixture "/site/shared-elements" with status 200 + And I load the page fixture with "/landingpage/home" + And the content rating form is enabled + Then the page endpoint for path "/page" returns the loaded fixture + + Given I visit the page "/page" + Then the content rating form should be displayed + And I click the content rating option labelled "Yes" + Then the content rating form should display the custom text "Test custom rating text." + + @mockserver + Scenario: Content Rating (hidden) + Given the site endpoint returns fixture "/site/shared-elements" with status 200 + And I load the page fixture with "/landingpage/home" + And the content rating form is disabled + Then the page endpoint for path "/page" returns the loaded fixture + + Given I visit the page "/page" + Then the content rating form should not be displayed + @mockserver Scenario: Footer Given the site endpoint returns fixture "/site/shared-elements" with status 200 diff --git a/examples/nuxt-app/test/features/site/theme.feature b/examples/nuxt-app/test/features/site/theme.feature index 3583731f94..28cd35ddb4 100644 --- a/examples/nuxt-app/test/features/site/theme.feature +++ b/examples/nuxt-app/test/features/site/theme.feature @@ -55,6 +55,20 @@ Feature: Site theme Given I visit the page "/" Then the last updated date should not be displayed + @mockserver + Scenario: Default behaviour for long link titles in the primary navigation + Given the site endpoint returns fixture "/site/primary-nav-wrap" with status 200 + And the page endpoint for path "/" returns fixture "/landingpage/image-banner" with status 200 + Given I visit the page "/" + Then the primary nav links should wrap + + @mockserver + Scenario: Feature flag to force multi-line links to render on a single line in the primary navigation + Given the site endpoint returns fixture "/site/primary-nav-nowrap" with status 200 + And the page endpoint for path "/" returns fixture "/landingpage/image-banner" with status 200 + Given I visit the page "/" + Then the primary nav links should not wrap + @mockserver Scenario: Favicon and app icons are set correctly Given the site endpoint returns fixture "/site/reference" with status 200 diff --git a/examples/nuxt-app/test/fixtures/landingpage/home.json b/examples/nuxt-app/test/fixtures/landingpage/home.json index ada9c00fd0..f2e982bbd0 100644 --- a/examples/nuxt-app/test/fixtures/landingpage/home.json +++ b/examples/nuxt-app/test/fixtures/landingpage/home.json @@ -907,6 +907,25 @@ } ] } + }, + { + "uuid": "5cb51ee5-a63f-433e-92a6-017b92ce37fe", + "component": "TideLandingPageMediaEmbed", + "id": "1951", + "props": { + "title": "Complex image", + "caption": "Source by the Department", + "src": "/placeholders/medium.png", + "dataLabel": "More info", + "dataContent": "

Ex eiusmod quis pariatur ipsum exercitation est velit eu magna.

", + "fullscreenLabel": "Fullscreen", + "downloadUrl": "/placeholders/medium.png", + "downloadLabel": "Download it", + "type": "image", + "variant": "complex", + "allowFullscreen": true, + "showTitle": true + } } ], "meta": { diff --git a/examples/nuxt-app/test/fixtures/maps/arcgis-address-candidates.json b/examples/nuxt-app/test/fixtures/maps/arcgis-address-candidates.json new file mode 100644 index 0000000000..87552915cb --- /dev/null +++ b/examples/nuxt-app/test/fixtures/maps/arcgis-address-candidates.json @@ -0,0 +1,23 @@ +{ + "spatialReference": { + "wkid": 4283, + "latestWkid": 4283 + }, + "candidates": [ + { + "address": "1234 FAKE ST, FAKEVILLE VIC 3000", + "location": { + "x": 143.49399077922746, + "y": -38.50295307104747 + }, + "score": 100, + "attributes": {}, + "extent": { + "xmin": 143.4921921351353, + "ymin": -38.50475171513962, + "xmax": 143.49578942331962, + "ymax": -38.501154426955324 + } + } + ] +} diff --git a/examples/nuxt-app/test/fixtures/maps/basic-page.json b/examples/nuxt-app/test/fixtures/maps/basic-page.json index 1a711df835..7bc59eb4d0 100644 --- a/examples/nuxt-app/test/fixtures/maps/basic-page.json +++ b/examples/nuxt-app/test/fixtures/maps/basic-page.json @@ -91,6 +91,7 @@ "index": "elasticsearch_index_develop_node", "resultsPerPage": 5, "displayMapTab": true, + "formTheme": "reverse", "labels": { "submit": "Search", "placeholder": "Search by postcode or suburb" @@ -196,12 +197,12 @@ "props": { "columns": [ { - "label": "School name", - "component": "TideSearchListingTableLink" + "label": "Title", + "objectKey": "test_title" }, { - "label": "Suburb", - "objectKey": "field_suburb" + "label": "Description", + "objectKey": "description" } ] } diff --git a/examples/nuxt-app/test/fixtures/maps/example-suburbs-response.json b/examples/nuxt-app/test/fixtures/maps/example-suburbs-response.json new file mode 100644 index 0000000000..2040bf329f --- /dev/null +++ b/examples/nuxt-app/test/fixtures/maps/example-suburbs-response.json @@ -0,0 +1,89 @@ +{ + "took": 5, + "timed_out": false, + "_shards": { + "total": 2, + "successful": 2, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 2, + "relation": "eq" + }, + "max_score": 1.0, + "hits": [ + { + "_index": ".ent-search-engine-documents-vicpol-postcode-localities", + "_id": "doc-661493669bd65fde1ab9b791", + "_score": 1.0, + "_ignored": [ + "name.date", + "bbox.location", + "bbox.date", + "center.date", + "lga.date", + "name.location", + "center.float", + "lga.float", + "name.float", + "lga.location", + "areatype.location", + "postcode.date", + "areatype.float", + "areatype.date" + ], + "_source": { + "name": "Bayswater North", + "areatype": "locality", + "center": "-37.8268821,145.2836623", + "bbox": [ + "145.25846667091363", + "-37.83800461846399", + "145.30593610877344", + "-37.813258938042594" + ], + "postcode": ["3153"], + "lga": ["Maroondah City"], + "id": "doc-661493669bd65fde1ab9b791" + } + }, + { + "_index": ".ent-search-engine-documents-vicpol-postcode-localities", + "_id": "doc-661493669bd65fde1ab9b790", + "_score": 1.0, + "_ignored": [ + "name.date", + "bbox.location", + "bbox.date", + "center.date", + "lga.date", + "name.location", + "center.float", + "lga.float", + "name.float", + "lga.location", + "areatype.location", + "postcode.date", + "areatype.float", + "areatype.date" + ], + "_source": { + "name": "Bayswater", + "areatype": "locality", + "center": "-37.845164,145.2647374", + "bbox": [ + "145.2382753619277", + "-37.85963511312151", + "145.28737155665652", + "-37.833007533571006" + ], + "postcode": ["3153"], + "lga": ["Knox City"], + "id": "doc-661493669bd65fde1ab9b790" + } + } + ] + } +} diff --git a/examples/nuxt-app/test/fixtures/publication/sample-publication-page.json b/examples/nuxt-app/test/fixtures/publication/sample-publication-page.json index 5d54128761..ea2462aafd 100644 --- a/examples/nuxt-app/test/fixtures/publication/sample-publication-page.json +++ b/examples/nuxt-app/test/fixtures/publication/sample-publication-page.json @@ -127,7 +127,7 @@ "documents": [ { "name": "Victorian Skills Plan Implementation Update October 2023", - "url": "https://master.content.vic.gov.au/sites/default/files/2023-10/16686-VSA-Implementation-Plan-Section_FA_Digital.pdf", + "url": "/sites/default/files/2023-10/16686-VSA-Implementation-Plan-Section_FA_Digital.pdf", "size": "4.61 MB", "extension": "pdf", "id": "64b1b6fc-083d-4570-8b0e-5f54a756a80c" diff --git a/examples/nuxt-app/test/fixtures/publication/sample-publication.json b/examples/nuxt-app/test/fixtures/publication/sample-publication.json index bbe43d6238..dfa0fd8916 100644 --- a/examples/nuxt-app/test/fixtures/publication/sample-publication.json +++ b/examples/nuxt-app/test/fixtures/publication/sample-publication.json @@ -199,7 +199,7 @@ "documents": [ { "name": "Victorian Skills Plan Implementation Update October 2023", - "url": "https://master.content.vic.gov.au/sites/default/files/2023-10/16686-VSA-Implementation-Plan-Section_FA_Digital.pdf", + "url": "/sites/default/files/2023-10/16686-VSA-Implementation-Plan-Section_FA_Digital.pdf", "size": "4.61 MB", "extension": "pdf", "id": "64b1b6fc-083d-4570-8b0e-5f54a756a80c" diff --git a/examples/nuxt-app/test/fixtures/search-listing/list/response.json b/examples/nuxt-app/test/fixtures/search-listing/list/response.json index 63c00c9aa0..8e30929414 100644 --- a/examples/nuxt-app/test/fixtures/search-listing/list/response.json +++ b/examples/nuxt-app/test/fixtures/search-listing/list/response.json @@ -321,6 +321,9 @@ "field_paragraph_body": [ "The Department of Premier and Cabinet, as owner of this website on behalf of the Victorian Government, is committed to providing a website that is accessible to the widest possible audience, regardless of technology or ability.  This website aims to meet level AA of the Web Content Accessibility Guidelines (WCAG) 2.1 World Wide Web Consortium (W3C) Web Content Accessibility Guidelines 2.0 . If there is information on this website that you can't access, or have any suggestions on how we can improve the accessibility of this website, please email us via  digital@dpc.vic.gov.au digital@dpc.vic.gov.au  or contact us by mail to: Department of Premier and Cabinet 1 Treasury Place East Melbourne VIC 3002 All constructive feedback regarding the accessibility or usability of this website is welcome and will be carefully considered. Assistance If you are a TTY user, phone 133 677 133 677 then ask for 1300 366 356 If you are a Speak and Listen user, phone 1300 555 727 1300 555 727 then ask for 1300 366 356 For SMS relay, use 0423 677 767 0423 677 767 If you are an internet relay user, visit the Internet Relay call page Make an Internet Relay call page and use this number: 1300 366 356" ], + "field_redirect_website": [ + "https://vic.gov.au/page" + ], "field_tags": [ 2095, 2096 diff --git a/examples/nuxt-app/test/fixtures/search-listing/table/page-extra-structured.json b/examples/nuxt-app/test/fixtures/search-listing/table/page-extra-structured.json index 4b1e5b9752..1a5e2e53d6 100644 --- a/examples/nuxt-app/test/fixtures/search-listing/table/page-extra-structured.json +++ b/examples/nuxt-app/test/fixtures/search-listing/table/page-extra-structured.json @@ -79,6 +79,7 @@ { "label": "Website", "objectKey": "Website/Links", + "objectTextKey": "Organisation", "component": "TideSearchListingTableUrl" }, { diff --git a/examples/nuxt-app/test/fixtures/site/primary-nav-nowrap.json b/examples/nuxt-app/test/fixtures/site/primary-nav-nowrap.json new file mode 100644 index 0000000000..0d8cffdf35 --- /dev/null +++ b/examples/nuxt-app/test/fixtures/site/primary-nav-nowrap.json @@ -0,0 +1,44 @@ +{ + "name": "Test site for neutral theme", + "acknowledgementHeader": "Test hero acknowledgement", + "acknowledgementFooter": "Test footer acknowledgement", + "socialImages": { + "twitter": {}, + "og": {} + }, + "siteLogo": { + "href": "/", + "src": "https://placehold.co/140x40", + "altText": "" + }, + "featureFlags": { + "primaryNavNowrap": true + }, + "menus": { + "menuMain": [ + { + "text": "This link has an excessively long title", + "url": "/demo-landing-page", + "uuid": "29bc9750-a335-455e-9e9a-4166c0bd73df", + "parent": null, + "weight": 0 + }, + { + "text": "Another random link", + "url": "/demo-landing-page", + "uuid": "04e44b77-20df-4a73-b0d1-cf2d3d614754", + "parent": null, + "weight": 0 + } + ], + "menuFooter": [ + { + "text": "Demo Landing Page", + "url": "/demo-landing-page", + "uuid": "04e44b77-20df-4a73-b0d1-cf2d3d614754", + "parent": null, + "weight": 0 + } + ] + } +} diff --git a/examples/nuxt-app/test/fixtures/site/primary-nav-wrap.json b/examples/nuxt-app/test/fixtures/site/primary-nav-wrap.json new file mode 100644 index 0000000000..0d133a87fe --- /dev/null +++ b/examples/nuxt-app/test/fixtures/site/primary-nav-wrap.json @@ -0,0 +1,41 @@ +{ + "name": "Test site for neutral theme", + "acknowledgementHeader": "Test hero acknowledgement", + "acknowledgementFooter": "Test footer acknowledgement", + "socialImages": { + "twitter": {}, + "og": {} + }, + "siteLogo": { + "href": "/", + "src": "https://placehold.co/140x40", + "altText": "" + }, + "menus": { + "menuMain": [ + { + "text": "This link has an excessively long title", + "url": "/demo-landing-page", + "uuid": "29bc9750-a335-455e-9e9a-4166c0bd73df", + "parent": null, + "weight": 0 + }, + { + "text": "Another random link", + "url": "/demo-landing-page", + "uuid": "04e44b77-20df-4a73-b0d1-cf2d3d614754", + "parent": null, + "weight": 0 + } + ], + "menuFooter": [ + { + "text": "Demo Landing Page", + "url": "/demo-landing-page", + "uuid": "04e44b77-20df-4a73-b0d1-cf2d3d614754", + "parent": null, + "weight": 0 + } + ] + } +} diff --git a/examples/nuxt-app/test/fixtures/site/search-request-content-types.json b/examples/nuxt-app/test/fixtures/site/search-request-content-types.json new file mode 100644 index 0000000000..3d58126ce5 --- /dev/null +++ b/examples/nuxt-app/test/fixtures/site/search-request-content-types.json @@ -0,0 +1,72 @@ +{ + "query": "", + "filters": { + "all": [ + { + "any": [ + { + "field_node_site": "TEST_SITE" + } + ] + }, + { + "any": [ + { + "type": [ + "landing_page", + "event", + "news", + "publication", + "publication_page", + "tide_search_listing", + "product" + ] + } + ] + } + ] + }, + "search_fields": { + "title": { + "weight": 10 + }, + "body": {}, + "field_paragraph_body": {}, + "field_landing_page_summary": {}, + "summary_processed": {}, + "field_paragraph_summary": {}, + "field_event_details_event_locality": {} + }, + "result_fields": { + "title": { + "raw": { + "size": 150 + } + }, + "field_landing_page_summary": { + "snippet": { + "size": 150, + "fallback": true + } + }, + "summary_processed": { + "snippet": { + "size": 150, + "fallback": true + } + }, + "changed": { + "raw": {} + }, + "url": { + "raw": {} + }, + "type": { + "raw": {} + } + }, + "page": { + "size": 10, + "current": 1 + } +} diff --git a/examples/nuxt-app/test/fixtures/site/shared-elements.json b/examples/nuxt-app/test/fixtures/site/shared-elements.json index c36fd0fd43..4a4c9fa138 100644 --- a/examples/nuxt-app/test/fixtures/site/shared-elements.json +++ b/examples/nuxt-app/test/fixtures/site/shared-elements.json @@ -5,6 +5,7 @@ "acknowledgementHeader": "The Victorian Government acknowledges Aboriginal Traditional Owners of Country throughout Victoria and pays respect to their cultures and Elders past, present and emerging.", "acknowledgementFooter": "Test footer acknowledgement", "copyrightHtml": "

Test footer copyright html

", + "contentRatingText": "

Test custom rating text.

", "footerLogos": [ { "alt": "Test logo 1", diff --git a/jest.config.js b/jest.config.js index 4729fb4e4a..eac7da33c3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,7 +13,9 @@ module.exports = { '^.+\\.ts$': '/node_modules/ts-jest', '.*\\.vue$': '/node_modules/@vue/vue3-jest' }, - transformIgnorePatterns: ['node_modules/(?!.pnpm)(?!(ripple-*|lodash-es)/)'], + transformIgnorePatterns: [ + 'node_modules/(?!.pnpm)(?!(ripple-*|lodash-es|cheerio)/)' + ], extensionsToTreatAsEsm: ['.ts'], moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', diff --git a/lerna.json b/lerna.json index c3d93e8857..fa975db385 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.11.0", + "version": "2.14.0", "npmClient": "pnpm", "exact": true, "command": { diff --git a/packages/eslint-config-ripple/package.json b/packages/eslint-config-ripple/package.json index 1a83192554..54faab5bb6 100644 --- a/packages/eslint-config-ripple/package.json +++ b/packages/eslint-config-ripple/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/eslint-config-ripple", "description": "ESLint config for Ripple projects", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "repository": "https://github.com/dpc-sdp/ripple-framework", "main": "index.js", diff --git a/packages/nuxt-ripple-analytics/lib/index.ts b/packages/nuxt-ripple-analytics/lib/index.ts index 93fa2b5046..c80b186d1e 100644 --- a/packages/nuxt-ripple-analytics/lib/index.ts +++ b/packages/nuxt-ripple-analytics/lib/index.ts @@ -170,7 +170,8 @@ export default { }, 'rpl-file/download': () => { return (payload: any) => { - const { host, pathname } = new URL(payload?.value) + const { $app_origin } = useNuxtApp() + const { host, pathname } = new URL(payload?.value, $app_origin) trackEvent({ event: `file_${payload.action}`, @@ -291,7 +292,8 @@ export default { }, 'rpl-media-embed/downloadImage': () => { return (payload: any) => { - const { pathname, host } = new URL(payload?.value) + const { $app_origin } = useNuxtApp() + const { pathname, host } = new URL(payload?.value, $app_origin) const fileName = pathname.split('/').pop() trackEvent({ diff --git a/packages/nuxt-ripple-analytics/package.json b/packages/nuxt-ripple-analytics/package.json index 637bc283ab..55d49d22d7 100644 --- a/packages/nuxt-ripple-analytics/package.json +++ b/packages/nuxt-ripple-analytics/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/nuxt-ripple-analytics", "description": "Nuxt module for handling event tracking.", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "main": "./nuxt.config.ts", "repository": "https://github.com/dpc-sdp/ripple-framework", diff --git a/packages/nuxt-ripple-cli/package.json b/packages/nuxt-ripple-cli/package.json index aab13a568c..9c5e9e9e80 100644 --- a/packages/nuxt-ripple-cli/package.json +++ b/packages/nuxt-ripple-cli/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/nuxt-ripple-cli", "description": "A CLI for simplifying common setup and scaffolding tasks", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "repository": "https://github.com/dpc-sdp/ripple-framework", "main": "./dist/index.js", diff --git a/packages/nuxt-ripple-cli/src/commands/init/_templates/layer/latest/github/publish.yml.t b/packages/nuxt-ripple-cli/src/commands/init/_templates/layer/latest/github/publish.yml.t index befccbe5c7..d796cb4058 100644 --- a/packages/nuxt-ripple-cli/src/commands/init/_templates/layer/latest/github/publish.yml.t +++ b/packages/nuxt-ripple-cli/src/commands/init/_templates/layer/latest/github/publish.yml.t @@ -14,9 +14,9 @@ jobs: packages: write contents: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: registry-url: 'https://npm.pkg.github.com/' node-version: 20 @@ -25,10 +25,10 @@ jobs: git config --global user.email "sdp.devs@dpc.vic.gov.au" git config --global user.name "SDP Deploy" - name: Bump version from release number - run: npm version ${{ github.event.release.name }} --allow-same-version + run: npm version ${{ github.event.release.name }} --no-git-tag-version env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Publish to GH Package registry - run: npm publish + run: npm publish --tag ${{ github.event.release.prerelease && 'alpha' || 'latest' }} env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/packages/nuxt-ripple-preview/package.json b/packages/nuxt-ripple-preview/package.json index 5513ef756c..67c4ff9f85 100644 --- a/packages/nuxt-ripple-preview/package.json +++ b/packages/nuxt-ripple-preview/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/nuxt-ripple-preview", "description": "Adds support for drupal preview links in Ripple frontend sites", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "main": "./nuxt.config.ts", "repository": "https://github.com/dpc-sdp/ripple-framework", diff --git a/packages/nuxt-ripple/app.config.ts b/packages/nuxt-ripple/app.config.ts index 83f9106604..ce7f3ff82e 100644 --- a/packages/nuxt-ripple/app.config.ts +++ b/packages/nuxt-ripple/app.config.ts @@ -46,9 +46,23 @@ declare module '@nuxt/schema' { > mapResultHooks?: Record< string, - (map: any, results: any, location: any) => void + (map: any, results: any, location: any, mapDeadSpace: any) => void > } + customInputs?: { + [key: string]: { + id: string + type: string + formkitDefProps: { + [key: string]: any + } + mapping: ( + fieldID: string, + field: any, + fieldKey: string + ) => Record + } + } } } } diff --git a/packages/nuxt-ripple/components/TideBaseLayout.vue b/packages/nuxt-ripple/components/TideBaseLayout.vue index 79be04bf6e..398959f507 100644 --- a/packages/nuxt-ripple/components/TideBaseLayout.vue +++ b/packages/nuxt-ripple/components/TideBaseLayout.vue @@ -63,6 +63,7 @@ @@ -111,8 +112,8 @@ export interface Props { footerImageCaption?: string topicTags?: TideTopicTag[] updatedDate?: string | null - siteSection: TideSiteSection | null - page: any + siteSection?: TideSiteSection | null + page?: any showContentRating?: boolean } @@ -123,6 +124,7 @@ const props = withDefaults(defineProps(), { topicTags: () => [], updatedDate: null, siteSection: null, + page: null, showContentRating: false }) diff --git a/packages/nuxt-ripple/components/TideContentRating.vue b/packages/nuxt-ripple/components/TideContentRating.vue index 7c10483b19..eccfdf2e08 100644 --- a/packages/nuxt-ripple/components/TideContentRating.vue +++ b/packages/nuxt-ripple/components/TideContentRating.vue @@ -3,10 +3,12 @@ import { onMounted, ref } from 'vue' interface Props { siteSectionName: string + contentRatingText: string } withDefaults(defineProps(), { - siteSectionName: '' + siteSectionName: '', + contentRatingText: '' }) // This is an actual webform that exists in drupal @@ -70,7 +72,7 @@ onMounted(() => { id="comments" type="RplFormTextarea" name="comments" - label="Enter your comments" + label="Enter your feedback" :maxlength="5000" :rows="4" :validation="[['matches', '/^\\W*(\\w+(\\W+|$)){1,500}$/']]" @@ -78,14 +80,11 @@ onMounted(() => { matches: 'You must enter between 1 and 500 words' }" /> - -

- If you need a response, please use our - contact us form. -

-
+ getImageFromField(src, 'field_bottom_corner_graphic') }, + contentRatingText: (src: any) => { + return src.hasOwnProperty('field_additional_comment') + ? getBodyFromField(src, 'field_additional_comment') + : '

If you need a response, please use our contact us form.

' + }, acknowledgementFooter: 'field_acknowledgement_to_country', copyrightHtml: (src: any) => { return getBody(src.field_site_footer_text?.processed) diff --git a/packages/nuxt-ripple/package.json b/packages/nuxt-ripple/package.json index 5554d1a930..68161d3266 100644 --- a/packages/nuxt-ripple/package.json +++ b/packages/nuxt-ripple/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/nuxt-ripple", "description": "Nuxt module for integrating Ripple and Tide", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "main": "./nuxt.config.ts", "repository": "https://github.com/dpc-sdp/ripple-framework", diff --git a/packages/nuxt-ripple/server/plugins/prefetch.ts b/packages/nuxt-ripple/server/plugins/prefetch.ts index d1d9f851f0..edc060719a 100644 --- a/packages/nuxt-ripple/server/plugins/prefetch.ts +++ b/packages/nuxt-ripple/server/plugins/prefetch.ts @@ -1,4 +1,4 @@ -import * as cheerio from 'cheerio' +import { load } from 'cheerio' import type { NitroApp } from 'nitropack' // fix type stub - See https://github.com/nuxt/nuxt/issues/18556 @@ -17,7 +17,7 @@ export default defineNitroPlugin((nitroApp) => { // https://github.com/nuxt/nuxt/issues/18376 html.head = html.head.map((head) => { - const $ = cheerio.load(head) + const $ = load(head) $('link[rel=prefetch]').each(function () { $(this).remove() }) diff --git a/packages/ripple-sdp-core/LICENSE b/packages/ripple-sdp-core/LICENSE new file mode 100644 index 0000000000..6b97259524 --- /dev/null +++ b/packages/ripple-sdp-core/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2018 Software Freedom Conservancy (SFC) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/ripple-sdp-core/README.md b/packages/ripple-sdp-core/README.md new file mode 100644 index 0000000000..013603a2a4 --- /dev/null +++ b/packages/ripple-sdp-core/README.md @@ -0,0 +1,24 @@ +# Ripple Sdp Core + +> SDP core content types. + +## Installation + +To use this package in your Nuxt project first install it with `npm`. + +```bash +npm install @dpc-sdp/ripple-sdp-core +``` + +## Usage + +Add the installed package in your sites `nuxt.config.js` file under the extends property, this includes the package as a [Nuxt Layer](https://nuxt.com/docs/getting-started/layers). +This will add front end support for the `grant` content type. + +```js +export default defineNuxtConfig({ + extends: [ + '@dpc-sdp/ripple-sdp-core' + ] +}) +``` diff --git a/packages/ripple-sdp-core/nuxt.config.ts b/packages/ripple-sdp-core/nuxt.config.ts new file mode 100644 index 0000000000..4d0bb03120 --- /dev/null +++ b/packages/ripple-sdp-core/nuxt.config.ts @@ -0,0 +1,15 @@ +import { defineNuxtConfig } from 'nuxt/config' + +export default defineNuxtConfig({ + extends: [ + '@dpc-sdp/ripple-tide-event', + '@dpc-sdp/ripple-tide-topic', + '@dpc-sdp/ripple-tide-landing-page', + '@dpc-sdp/ripple-tide-grant', + '@dpc-sdp/ripple-tide-publication', + '@dpc-sdp/ripple-tide-media', + '@dpc-sdp/ripple-tide-news', + '@dpc-sdp/ripple-tide-search', + '@dpc-sdp/ripple-tide-webform' + ] +}) diff --git a/packages/ripple-sdp-core/package.json b/packages/ripple-sdp-core/package.json new file mode 100644 index 0000000000..fb8b37e982 --- /dev/null +++ b/packages/ripple-sdp-core/package.json @@ -0,0 +1,22 @@ +{ + "name": "@dpc-sdp/ripple-sdp-core", + "description": "SDP core content types", + "version": "2.14.0", + "license": "Apache-2.0", + "repository": "https://github.com/dpc-sdp/ripple-framework", + "main": "./nuxt.config.ts", + "exports": { + ".": "./nuxt.config.ts" + }, + "dependencies": { + "@dpc-sdp/ripple-tide-event": "workspace:*", + "@dpc-sdp/ripple-tide-grant": "workspace:*", + "@dpc-sdp/ripple-tide-landing-page": "workspace:*", + "@dpc-sdp/ripple-tide-media": "workspace:*", + "@dpc-sdp/ripple-tide-news": "workspace:*", + "@dpc-sdp/ripple-tide-publication": "workspace:*", + "@dpc-sdp/ripple-tide-search": "workspace:*", + "@dpc-sdp/ripple-tide-topic": "workspace:*", + "@dpc-sdp/ripple-tide-webform": "workspace:*" + } +} diff --git a/packages/ripple-sdp-core/tsconfig.json b/packages/ripple-sdp-core/tsconfig.json new file mode 100644 index 0000000000..2403fca1a1 --- /dev/null +++ b/packages/ripple-sdp-core/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./../../examples/nuxt-app/.nuxt/tsconfig.json" +} diff --git a/packages/ripple-storybook/package.json b/packages/ripple-storybook/package.json index 31f87ebba8..a32cb6c4ae 100644 --- a/packages/ripple-storybook/package.json +++ b/packages/ripple-storybook/package.json @@ -1,7 +1,7 @@ { "name": "ripple-storybook", "description": "Ripple Storybook instance", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "private": true, "repository": "https://github.com/dpc-sdp/ripple-framework", diff --git a/packages/ripple-test-utils/package.json b/packages/ripple-test-utils/package.json index 95f55050fa..9a5d32bcf1 100644 --- a/packages/ripple-test-utils/package.json +++ b/packages/ripple-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/ripple-test-utils", "description": "Test utils for Ripple sites", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "type": "module", "main": "./dist/config/index.js", diff --git a/packages/ripple-test-utils/step_definitions/common/index.ts b/packages/ripple-test-utils/step_definitions/common/index.ts index b914f08684..b0342dfe77 100644 --- a/packages/ripple-test-utils/step_definitions/common/index.ts +++ b/packages/ripple-test-utils/step_definitions/common/index.ts @@ -2,6 +2,7 @@ import './navigation' import './mocks' import './site/alerts' import './site/theme' +import './site/analytics' import './shared-elements' import './sitemap' import './languages' diff --git a/packages/ripple-test-utils/step_definitions/common/shared-elements.ts b/packages/ripple-test-utils/step_definitions/common/shared-elements.ts index d28b2e372a..c0a041b15b 100644 --- a/packages/ripple-test-utils/step_definitions/common/shared-elements.ts +++ b/packages/ripple-test-utils/step_definitions/common/shared-elements.ts @@ -58,6 +58,17 @@ Then('the last updated date should not be displayed', () => { cy.get(`[data-cy="updated-date"]`).should('not.exist') }) +Then('the primary nav links should wrap', () => { + cy.get('.rpl-primary-nav__nav-bar-actions-list--nowrap').should('not.exist') +}) + +Then('the primary nav links should not wrap', () => { + cy.get('.rpl-primary-nav__nav-bar-actions-list').should( + 'have.class', + 'rpl-primary-nav__nav-bar-actions-list--nowrap' + ) +}) + Then( 'the page should have the following topic tags', (dataTable: DataTable) => { @@ -215,6 +226,8 @@ Then('the footer should have the following logos', (dataTable: DataTable) => { Given( 'the feature flag {string} is set to {string}', (flag: string, value: string) => { + if (value === 'true') value = true + if (value === 'false') value = false cy.get('@siteFixture').then((response) => { set(response, `featureFlags.${flag}`, value) }) @@ -241,6 +254,37 @@ Then('the quick exit should not be displayed', () => { cy.get('.rpl-primary-nav__quick-exit').should('not.exist') }) +Then('the content rating form should be displayed', () => { + cy.get('.tide-content-rating').should('be.visible') +}) + +Then('the content rating form should not be displayed', () => { + cy.get('.tide-content-rating').should('not.exist') +}) + +Then( + 'the content rating form should display the custom text {string}', + (text: string) => { + cy.get('.tide-content-rating__text').should('have.text', text) + } +) + +Then('I click the content rating option labelled {string}', (label: string) => { + cy.get('.tide-content-rating label').contains(label).click() +}) + +Given('the content rating form is enabled', () => { + cy.get('@pageFixture').then((response) => { + set(response, 'showContentRating', true) + }) +}) + +Given('the content rating form is disabled', () => { + cy.get('@pageFixture').then((response) => { + set(response, 'showContentRating', false) + }) +}) + Given('the site sections share links are set to included WhatsApp', () => { cy.get('@pageFixture').then((response) => { set( @@ -266,3 +310,7 @@ Then('the in page navigation should include', (dataTable: DataTable) => { }) }) }) + +Given('I click on the document {string}', (label: string) => { + cy.contains('.rpl-document__link', label).trigger('click') +}) diff --git a/packages/ripple-test-utils/step_definitions/common/site/analytics.ts b/packages/ripple-test-utils/step_definitions/common/site/analytics.ts new file mode 100644 index 0000000000..bb3bf741e0 --- /dev/null +++ b/packages/ripple-test-utils/step_definitions/common/site/analytics.ts @@ -0,0 +1,25 @@ +import { DataTable, Then } from '@badeball/cypress-cucumber-preprocessor' + +Then( + 'the dataLayer should include the following events', + (dataTable: DataTable) => { + const table = dataTable.hashes() + + table.forEach((row) => { + cy.window().then((window) => { + const dataLayer = window.dataLayer + + const event = dataLayer.find((i) => i.event === row.event) + + const updatedRow = Object.entries(row).reduce((acc, [key, value]) => { + return { + ...acc, + [key]: value + } + }, {}) + + cy.wrap(event).should('include', updatedRow) + }) + }) + } +) diff --git a/packages/ripple-test-utils/step_definitions/components/accordion.ts b/packages/ripple-test-utils/step_definitions/components/accordion.ts index 553a2e68c2..7d1d9dbb83 100644 --- a/packages/ripple-test-utils/step_definitions/components/accordion.ts +++ b/packages/ripple-test-utils/step_definitions/components/accordion.ts @@ -43,6 +43,13 @@ Then( } ) +Then( + 'I click the accordion item {string} in accordion with ID {string}', + (text: string, id: string) => { + cy.get(`#${id}`).contains('button', text).click() + } +) + When( 'I click the open all button on accordion with ID {string}', (id: string) => { diff --git a/packages/ripple-test-utils/step_definitions/components/forms.ts b/packages/ripple-test-utils/step_definitions/components/forms.ts index 1155541e2b..a11c1b40ef 100644 --- a/packages/ripple-test-utils/step_definitions/components/forms.ts +++ b/packages/ripple-test-utils/step_definitions/components/forms.ts @@ -349,8 +349,25 @@ Then( } ) -// Open forms +// Extended +Then( + 'a field labelled {string} should exist with the CSS class {string}', + (label: string, css: string) => { + cy.get(css) + .closest('.rpl-form__outer') + .find('label.rpl-form-label') + .should('contain', label) + } +) + +// Unsupported +Then('an input of type {string} is not yet supported', (inputType: string) => { + cy.get('.rpl-form__outer.rpl-form__input--unsupported') + .contains(inputType) + .should('exist') +}) +// Open forms Then('there is an openforms embed with the url {string}', (url: string) => { cy.get(`iframe[src^="${url}"]`).should('exist') }) diff --git a/packages/ripple-test-utils/step_definitions/components/index.ts b/packages/ripple-test-utils/step_definitions/components/index.ts index 9628c9c333..ce7c381f0e 100644 --- a/packages/ripple-test-utils/step_definitions/components/index.ts +++ b/packages/ripple-test-utils/step_definitions/components/index.ts @@ -7,6 +7,7 @@ import './intro-banner' import './key-dates' import './maps' import './media-gallery' +import './media-embed' import './modals' import './promo-card' import './nav-card' diff --git a/packages/ripple-test-utils/step_definitions/components/maps.ts b/packages/ripple-test-utils/step_definitions/components/maps.ts index 665359701c..3b73080da2 100644 --- a/packages/ripple-test-utils/step_definitions/components/maps.ts +++ b/packages/ripple-test-utils/step_definitions/components/maps.ts @@ -1,4 +1,9 @@ -import { Then, When, Given } from '@badeball/cypress-cucumber-preprocessor' +import { + Then, + When, + Given, + DataTable +} from '@badeball/cypress-cucumber-preprocessor' import { set } from 'lodash-es' Then(`the ripple map component should be visible`, () => { @@ -9,7 +14,7 @@ Then(`the data map component tabs should exist`, () => { cy.get('.rpl-tabs').should('exist') }) -Then(`the data map tabs should be labelled:`, (dataTable) => { +Then(`the data map tabs should be labelled:`, (dataTable: DataTable) => { const items = dataTable.raw() cy.get('.rpl-tabs .rpl-tab').as('items') items.forEach((row, i: number) => { @@ -125,3 +130,91 @@ Given('I click the side panel item with text {string}', (title) => { .contains(title) .click({ force: true }) }) + +Given('a custom suggestions function called {string} is used', (fnName) => { + cy.get('@pageFixture').then((response) => { + set( + response, + 'bodyComponents[0].props.locationQueryConfig.props.suggestionsConfig', + { + function: fnName, + args: { + testArg: 'testValue' + } + } + ) + }) +}) + +When(`I type {string} into the location search bar`, (inputStr: string) => { + cy.get(`[id="tide-address-lookup"]`).focus() + cy.get(`[id="tide-address-lookup"]`).type(`${inputStr}`) + cy.get(`[id="tide-address-lookup"]`).focus() +}) + +Given( + 'the location autocomplete request is stubbed with {string} fixture', + (fixture: string) => { + cy.intercept('POST', `/api/tide/app-search/**/elasticsearch/_search`, { + statusCode: 200, + fixture + }).as('autocompleteRequest') // assign an alias + } +) + +Then( + 'the URL should reflect that the location has the following:', + (dataTable) => { + const items = dataTable.hashes() + + cy.location().should((loc) => { + const params = new URLSearchParams(loc.search) + + items.forEach((row) => { + expect(params.get(`location[${row.key}]`)).to.eq(`${row.value}`) + }) + }) + } +) + +Given( + 'the ArcGIS findAddressCandidates endpoint returns {string} fixture', + (fixture: string) => { + cy.intercept( + 'GET', + `https://corp-geo.mapshare.vic.gov.au/arcgis/rest/services/Geocoder/VMAddressEZIAdd/GeocodeServer/findAddressCandidates*`, + { + statusCode: 200, + fixture + } + ) + } +) + +Given(`the geolocation button is not enabled`, () => { + cy.get('@pageFixture').then((response) => { + set( + response, + 'bodyComponents[0].props.locationQueryConfig.showGeolocationButton', + false + ) + }) +}) + +Given(`the geolocation button is enabled`, () => { + cy.get('@pageFixture').then((response) => { + set( + response, + 'bodyComponents[0].props.locationQueryConfig.showGeolocationButton', + true + ) + }) +}) + +Given(`the geolocate button is hidden`, () => { + cy.get('.rpl-map-geolocate__btn').should('not.exist') +}) + +Given(`the geolocate button is displayed`, () => { + cy.get('.rpl-map-geolocate__btn').should('exist') +}) diff --git a/packages/ripple-test-utils/step_definitions/components/media-embed.ts b/packages/ripple-test-utils/step_definitions/components/media-embed.ts new file mode 100644 index 0000000000..129fcbe60e --- /dev/null +++ b/packages/ripple-test-utils/step_definitions/components/media-embed.ts @@ -0,0 +1,66 @@ +import { Given, Then } from '@badeball/cypress-cucumber-preprocessor' + +Given('a media embed with ID {string} should exist', (id: string) => { + cy.get(`[data-component-id="${id}"]`).as('component') + cy.get('@component').should('exist') + cy.get(`@component`).should( + 'have.attr', + 'data-component-type', + 'TideLandingPageMediaEmbed' + ) +}) + +Then( + 'I click the action {string} for media embed {string}', + (label: string, id: string) => { + cy.get(`[data-component-id="${id}"]`).as('component') + + cy.get('@component').within(() => { + cy.contains('.rpl-media-embed__action', label).click() + }) + } +) + +Then( + 'I trigger a click for the action {string} on the media embed {string}', + (label: string, id: string) => { + cy.get(`[data-component-id="${id}"]`).as('component') + + cy.get('@component').within(() => { + cy.contains('.rpl-media-embed__action', label).trigger('click') + }) + } +) + +Then( + 'the media embed image for {string} should be {string}', + (id: string, image: string) => { + cy.get(`[data-component-id="${id}"]`).as('component') + + cy.get('@component').within(() => { + cy.get('.rpl-media-embed__image').should('have.attr', 'src', image) + }) + } +) + +Then( + 'the extra data for media embed {string} should be visible', + (id: string) => { + cy.get(`[data-component-id="${id}"]`).as('component') + + cy.get('@component').within(() => { + cy.get('.rpl-media-embed__view-data-content').should('be.visible') + }) + } +) + +Then( + 'the extra data for media embed {string} should be hidden', + (id: string) => { + cy.get(`[data-component-id="${id}"]`).as('component') + + cy.get('@component').within(() => { + cy.get('.rpl-media-embed__view-data-content').should('be.hidden') + }) + } +) diff --git a/packages/ripple-test-utils/step_definitions/content-types/listing.ts b/packages/ripple-test-utils/step_definitions/content-types/listing.ts index 9da13a9b94..443dbb75f7 100644 --- a/packages/ripple-test-utils/step_definitions/content-types/listing.ts +++ b/packages/ripple-test-utils/step_definitions/content-types/listing.ts @@ -467,7 +467,7 @@ Then( `the search suggestions displayed should include`, (dataTable: DataTable) => { const table = dataTable.raw() - cy.get('#tide-search-bar__menu') + cy.get('.rpl-search-bar__menu') .find('[role="option"]') .as('suggestedOptions') @@ -483,7 +483,7 @@ Then( ) When('I click the search suggestion labelled {string}', (label: string) => { - cy.get('#tide-search-bar__menu') + cy.get('.rpl-search-bar__menu') .find('[role="option"]') .contains(label) .click() diff --git a/packages/ripple-test-utils/step_definitions/content-types/publication.ts b/packages/ripple-test-utils/step_definitions/content-types/publication.ts index dd5efbe2c3..79082b1a0a 100644 --- a/packages/ripple-test-utils/step_definitions/content-types/publication.ts +++ b/packages/ripple-test-utils/step_definitions/content-types/publication.ts @@ -70,3 +70,36 @@ Then( }) } ) + +Then( + 'the publication should display the following documents', + (dataTable: DataTable) => { + const table = dataTable.hashes() + + cy.get(`.tide-publication__sidebar .rpl-document`).as('items') + + table.forEach((row, i: number) => { + cy.get('@items') + .eq(i) + .then((item) => { + cy.wrap(item).as('item') + + cy.get('@item').find('a').as('link') + cy.get('@link').contains(row.title) + cy.get('@link') + .should('have.attr', 'href') + .then((href) => { + expect(href).to.contain(row.url) + }) + + if (row.type) { + cy.get('@item').find('.rpl-file__meta').contains(row.type) + } + + if (row.size) { + cy.get('@item').find('.rpl-file__meta').contains(row.size) + } + }) + }) + } +) diff --git a/packages/ripple-tide-api/package.json b/packages/ripple-tide-api/package.json index 39fa001654..fa0c6a1df0 100644 --- a/packages/ripple-tide-api/package.json +++ b/packages/ripple-tide-api/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/ripple-tide-api", "description": "Ripple API endpoints for Tide Drupal backend", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "repository": "https://github.com/dpc-sdp/ripple-framework", "main": "./dist/index.js", @@ -26,7 +26,7 @@ "@nuxt/kit": "3.11.2", "axios": "^1.3.4", "change-case": "^4.1.2", - "cheerio": "^1.0.0-rc.10", + "cheerio": "^1.0.0", "h3": "^1.9.0", "js-yaml": "^4.1.0", "jsonapi-parse": "^2.0.1", @@ -41,7 +41,7 @@ }, "devDependencies": { "@nuxt/schema": "3.11.2", - "@types/cheerio": "^0.22.31", + "@types/cheerio": "^0.22.35", "axios-mock-adapter": "^1.21.3", "defu": "^6.1.2", "rimraf": "^4.4.1" diff --git a/packages/ripple-tide-api/src/index.ts b/packages/ripple-tide-api/src/index.ts index 4dccd3e154..c660fb64eb 100644 --- a/packages/ripple-tide-api/src/index.ts +++ b/packages/ripple-tide-api/src/index.ts @@ -5,6 +5,7 @@ export { default as TideSiteApi } from './services/tide-site.js' export { default as logger } from './logger/logger.js' export * from './utils/createHandler.js' export { stripSiteId } from './utils/stripSiteId.js' +export { stripMediaBaseUrl } from './utils/stripMediaBaseUrl.js' export { addAnchorLinksToHTML, diff --git a/packages/ripple-tide-api/src/services/tide-page.ts b/packages/ripple-tide-api/src/services/tide-page.ts index 73a4aad296..168cb8afe4 100644 --- a/packages/ripple-tide-api/src/services/tide-page.ts +++ b/packages/ripple-tide-api/src/services/tide-page.ts @@ -327,10 +327,10 @@ export default class TidePageApi extends TideApiBase { async getEntityList( entityType, bundle, - filtering, - includes, - pagination, - sorting, + filtering?, + includes?, + pagination?, + sorting?, { allPages = true } = {} ) { const params: Record = { @@ -442,20 +442,8 @@ export default class TidePageApi extends TideApiBase { } async getTaxonomy(taxonomyName: string) { - const params = { - site: this.site - } try { - const { data: response } = await this.get( - `/taxonomy_term/${taxonomyName}`, - { - params - } - ) - if (response) { - const resource = jsonapiParse.parse(response).data - return resource - } + return await this.getEntityList('taxonomy_term', taxonomyName) } catch (error: any) { throw new ApplicationError('Error fetching taxonomy', { cause: error }) } diff --git a/packages/ripple-tide-api/src/utils.ts b/packages/ripple-tide-api/src/utils.ts index 14b5091754..82ee8f5c48 100644 --- a/packages/ripple-tide-api/src/utils.ts +++ b/packages/ripple-tide-api/src/utils.ts @@ -1 +1,2 @@ export { getDpcPkgs } from './utils/index.js' +export { stripMediaBaseUrl } from './utils/stripMediaBaseUrl.js' diff --git a/packages/ripple-tide-api/src/utils/mapping-utils.test.ts b/packages/ripple-tide-api/src/utils/mapping-utils.test.ts index 2f05d547a3..cc2bb58569 100644 --- a/packages/ripple-tide-api/src/utils/mapping-utils.test.ts +++ b/packages/ripple-tide-api/src/utils/mapping-utils.test.ts @@ -1,6 +1,5 @@ import { expect, describe } from '@jest/globals' import { - removeDomainFromPath, getImageFromField, getCardImageFromField, getLinkFromField, @@ -129,7 +128,7 @@ const field = { y: 50 }, height: 700, - src: 'https://develop.content.reference.sdp.vic.gov.au/sites/default/files/tide_demo_content/Melbourne-tram.jpg', + src: '/sites/default/files/tide_demo_content/Melbourne-tram.jpg', title: 'Demo: Melbourne tram', width: 900 }, @@ -140,18 +139,12 @@ const field = { y: 300 }, height: 785, - src: 'https://develop.content.reference.sdp.vic.gov.au/sites/default/files/tide_demo_content/Melbourne-tram.jpg', + src: '/sites/default/files/tide_demo_content/Melbourne-tram.jpg', title: 'Demo: Melbourne tram', width: 1413 } describe('ripple-tide-api/mapping utils', () => { - it(`removes domain from a given path`, () => { - expect( - removeDomainFromPath('https://domain.com/sites/default/files') - ).toEqual('/sites/default/files') - }) - it(`processes api field data for images`, () => { expect(getImageFromField(field, 'field_media_image')).toEqual(processedImg) }) diff --git a/packages/ripple-tide-api/src/utils/mapping-utils.ts b/packages/ripple-tide-api/src/utils/mapping-utils.ts index 92cc4638a2..66007a0893 100644 --- a/packages/ripple-tide-api/src/utils/mapping-utils.ts +++ b/packages/ripple-tide-api/src/utils/mapping-utils.ts @@ -1,7 +1,8 @@ import { get } from 'lodash-es' -import { TideImageField, TideUrlField } from '../../types' +import { TideImageField, TideUrlField, TideDocumentField } from '../../types' import markupTranspiler from './markup-transpiler/index.js' -import normaliseImageUrl from './normaliseImageUrl.js' +import { stripMediaBaseUrl } from './stripMediaBaseUrl.js' +import mime from 'mime-types' export type drupalField = Record @@ -46,13 +47,21 @@ type tidePageSitePartial = { ] } -/** - * @deprecated Need to make a decision on whether we proxy images or use direct url - */ -export const removeDomainFromPath = (path: string) => - typeof path === 'string' && path.length > 0 - ? path.replace(/^.*(?=(\/sites\/default\/files))/, '') - : path +export const getMediaPath = (field: any, path?: string | string[]): string => { + let uri = '' + + if (path) { + field = get(field, path) + } + + if (!field?.uri) { + uri = field?.url + } else { + uri = field?.uri?.url || field?.uri + } + + return stripMediaBaseUrl(uri, process.env.NUXT_PUBLIC_TIDE_BASE_URL as string) +} export const getImageFromField = ( field: object, @@ -85,10 +94,7 @@ export const getMediaImage = ( } return { - src: normaliseImageUrl( - process.env.NUXT_PUBLIC_TIDE_BASE_URL as string, - fieldMediaImage.url - ), + src: getMediaPath(fieldMediaImage), ...fieldMediaImage.meta, focalPoint } @@ -105,10 +111,7 @@ export const getCardImage = (fieldMediaImage: RawCardImage): TideImageField => { : null return { - src: normaliseImageUrl( - process.env.NUXT_PUBLIC_TIDE_BASE_URL as string, - fieldMediaImage.url - ), + src: getMediaPath(fieldMediaImage), focalPoint, alt: data?.alt, width: data?.width ? parseInt(data.width) : undefined, @@ -117,6 +120,21 @@ export const getCardImage = (fieldMediaImage: RawCardImage): TideImageField => { } } +export const getDocumentFromField = ( + field: drupalField, + path = 'field_media_file' +): TideDocumentField => { + const medaFile = get(field, path) + + return { + id: field.id, + name: field.name, + url: getMediaPath(medaFile), + extension: mime.extension(medaFile.filemime) || '', + size: humanizeFilesize(medaFile.filesize) + } +} + export const getLinkFromField = ( field: drupalField, path?: string | string[] @@ -230,6 +248,8 @@ export default { getBodyFromField, humanizeFilesize, getField, + getMediaPath, + getDocumentFromField, getSiteKeyValues, getSiteSection } diff --git a/packages/ripple-tide-api/src/utils/markup-transpiler/cheerio.test.ts b/packages/ripple-tide-api/src/utils/markup-transpiler/cheerio.test.ts index 70c0c026f1..2cfe80fa21 100644 --- a/packages/ripple-tide-api/src/utils/markup-transpiler/cheerio.test.ts +++ b/packages/ripple-tide-api/src/utils/markup-transpiler/cheerio.test.ts @@ -11,12 +11,14 @@ const markup = { callout: `

Hey it's a callout

And another callout

This one is wysiwyg
`, quotation: `

It was the best of times, it was the blurst of times.

Chimp 273
`, - document: ``, + document: ``, video: `
Caption goes here`, - image: `
Image
`, + image: `
Image
`, button: `Button`, link: `Link`, - iframe: `` + list: `
  • List item
  • List item
  • List item
  1. List item
  1. List item
  1. List item
  1. List item
`, + iframe: ``, + alignment: `

Left

Center

Right

` } const fixed = { @@ -36,7 +38,7 @@ const fixed = { `, document: `
- + @@ -65,10 +67,12 @@ const fixed = { `, - image: `
Image
`, + image: `
Image
`, button: `
Button`, link: `Link(opens in a new window)`, - iframe: `
` + list: `
  • List item
  • List item
  • List item
  1. List item
  1. List item
  1. List item
  1. List item
`, + iframe: `
`, + alignment: `

Left

Center

Right

` } describe('ripple-tide-api/utils/markup-transpiler/cheerio', () => { diff --git a/packages/ripple-tide-api/src/utils/markup-transpiler/default-plugins.ts b/packages/ripple-tide-api/src/utils/markup-transpiler/default-plugins.ts index 56f2fae691..5572d5519d 100644 --- a/packages/ripple-tide-api/src/utils/markup-transpiler/default-plugins.ts +++ b/packages/ripple-tide-api/src/utils/markup-transpiler/default-plugins.ts @@ -1,4 +1,5 @@ import { epochToDate } from '../epochToDate.js' +import { stripMediaBaseUrl } from '../stripMediaBaseUrl.js' const pluginTables = function (this: any) { // Wrap tables with a div. @@ -86,7 +87,10 @@ const pluginDocuments = function (this: any) { mediaType === 'document' ? '.file--title' : '.field--name-name' const label = $element.find('a[aria-label]').attr('aria-label'), - link = $element.find('a').attr('href'), + link = stripMediaBaseUrl( + $element.find('a').attr('href'), + process.env.NUXT_PUBLIC_TIDE_BASE_URL as string + ), title = $element.find(titleSelector).text(), fileSize = $element.find('.file--size').text(), updated = $element.attr('data-last-updated') @@ -189,7 +193,10 @@ const pluginImages = function (this: any) { this.find('.embedded-entity--media--image').map((i: any, el: any) => { const $img = this.find(el).find('img') const width = $img.attr('width') - const src = $img.attr('src') + const src = stripMediaBaseUrl( + $img.attr('src'), + process.env.NUXT_PUBLIC_TIDE_BASE_URL as string + ) const alt = $img.attr('alt') const $caption = this.find(el) .find('div.field--name-field-media-caption') @@ -248,6 +255,32 @@ const pluginLinks = function (this: any) { }) } +const pluginLists = function (this: any) { + this.find('ul[type], ol[type]').map((i: any, el: any) => { + const $list = this.find(el) + const type = $list.attr('type') + + const listTypes = { + 1: 'decimal', + a: 'lower-latin', + A: 'upper-latin', + i: 'lower-roman', + I: 'upper-roman', + disc: 'disc', + square: 'square', + circle: 'disc' // circles selection uses disc (a11y request) + } + + $list.removeAttr('type') + + if (listTypes[type]) { + $list.addClass(`rpl-type-list-${el?.name}--${listTypes[type]}`) + } + + return $list + }) +} + const pluginIFrames = function (this: any) { this.find('iframe').map((i: any, el: any) => { const $iframe = this.find(el) @@ -268,6 +301,25 @@ const pluginIFrames = function (this: any) { }) } +const pluginTextAlign = function (this: any) { + this.find('.text-align-left, .text-align-center, .text-align-right').map( + (i: any, el: any) => { + const $el = this.find(el) + const alignments = ['left', 'center', 'right'] + + alignments.forEach((alignment) => { + if ($el.hasClass(`text-align-${alignment}`)) { + $el + .removeClass(`text-align-${alignment}`) + .addClass(`rpl-u-text-${alignment}`) + } + }) + + return $el + } + ) +} + export default [ pluginTables, pluginCallout, @@ -277,5 +329,7 @@ export default [ pluginImages, pluginButtons, pluginLinks, - pluginIFrames + pluginLists, + pluginIFrames, + pluginTextAlign ] diff --git a/packages/ripple-tide-api/src/utils/markup-transpiler/index.ts b/packages/ripple-tide-api/src/utils/markup-transpiler/index.ts index 0951befc75..6da8b383c4 100644 --- a/packages/ripple-tide-api/src/utils/markup-transpiler/index.ts +++ b/packages/ripple-tide-api/src/utils/markup-transpiler/index.ts @@ -1,4 +1,4 @@ -import cheerio from 'cheerio' +import { load } from 'cheerio' import defaultPlugins from './default-plugins.js' // A markup transpiler for converting HTML into Vue template by giving plugins. @@ -10,7 +10,7 @@ const markupTranspiler = ( if (!html) { return '' } - const $ = cheerio.load(html, options) + const $ = load(html, options) const $body = $('body') let markupData = {} diff --git a/packages/ripple-tide-api/src/utils/stripMediaBaseUrl.test.ts b/packages/ripple-tide-api/src/utils/stripMediaBaseUrl.test.ts new file mode 100644 index 0000000000..559f3d130c --- /dev/null +++ b/packages/ripple-tide-api/src/utils/stripMediaBaseUrl.test.ts @@ -0,0 +1,57 @@ +import { expect, describe, it } from '@jest/globals' + +import stripMediaBaseUrl from './stripMediaBaseUrl.js' + +describe('stripMediaBaseUrl(url, baseUrl)', () => { + it('strips the supplied baseUrl from the main url', () => { + expect( + stripMediaBaseUrl( + 'https://example.com/sites/default/files/wow/cool.jpg?some=param', + 'https://example.com' + ) + ).toEqual('/sites/default/files/wow/cool.jpg?some=param') + + expect( + stripMediaBaseUrl( + 'https://example.com/sites/default/files/wow/cool.jpg?some=param', + 'https://example.com/' + ) + ).toEqual('/sites/default/files/wow/cool.jpg?some=param') + }) + + it('does not change a url if it points to an external domain', () => { + expect( + stripMediaBaseUrl( + 'https://example.com/sites/default/files/wow/cool.jpg?some=param', + 'https://google.com' + ) + ).toEqual('https://example.com/sites/default/files/wow/cool.jpg?some=param') + }) + + it('leaves relative URLs unchanged if the domains differ', () => { + expect( + stripMediaBaseUrl( + 'https://example.com/sites/default/files/wow/cool.jpg?some=param', + 'https://google.com' + ) + ).toEqual('https://example.com/sites/default/files/wow/cool.jpg?some=param') + }) + + it('leaves relative URLs unchanged if no baseUrl is passed', () => { + expect( + stripMediaBaseUrl( + 'https://example.com/sites/default/files/wow/cool.jpg?some=param', + '' + ) + ).toEqual('https://example.com/sites/default/files/wow/cool.jpg?some=param') + }) + + it('only strips the base URL for media files matching sites/default/files', () => { + expect( + stripMediaBaseUrl( + 'https://example.com/sites/tmp/files/wow/cool.jpg', + 'https://example.com' + ) + ).toEqual('https://example.com/sites/tmp/files/wow/cool.jpg') + }) +}) diff --git a/packages/ripple-tide-api/src/utils/stripMediaBaseUrl.ts b/packages/ripple-tide-api/src/utils/stripMediaBaseUrl.ts new file mode 100644 index 0000000000..bc4225e59b --- /dev/null +++ b/packages/ripple-tide-api/src/utils/stripMediaBaseUrl.ts @@ -0,0 +1,13 @@ +export function stripMediaBaseUrl(url: string, baseUrl: string): string { + if (!url || typeof url !== 'string' || typeof baseUrl !== 'string') { + return url + } + + if (!url.includes('/sites/default/files/')) { + return url + } + + return url.replace(baseUrl.replace(/\/$/, ''), '') +} + +export default stripMediaBaseUrl diff --git a/packages/ripple-tide-api/types.d.ts b/packages/ripple-tide-api/types.d.ts index 38055c2467..378076d319 100644 --- a/packages/ripple-tide-api/types.d.ts +++ b/packages/ripple-tide-api/types.d.ts @@ -30,6 +30,7 @@ export interface TideSiteData { top?: TideImageField bottom?: TideImageField } + contentRatingText?: string acknowledgementHeader?: string acknowledgementFooter: string copyrightHtml: string @@ -84,6 +85,14 @@ export interface TideImageField { } } +export type TideDocumentField = { + name: string + url: string + extension: string + size: string + id: string +} + export interface TidePageBase { title: string created: string @@ -255,6 +264,10 @@ export interface IRplFeatureFlags { * @description Option to disable the display of the search form within the primary navigation */ disablePrimaryNavSearch?: boolean + /** + * @description Force multi-line links to render on a single line in the primary navigation + */ + primaryNavNowrap?: boolean /** * @description Option to override the default URL the search for redirects to */ @@ -283,6 +296,14 @@ export interface IRplFeatureFlags { * @description Sets the number of toggle-able levels */ sectionNavToggleLevels?: number + /** + * @description Allow overriding the default site search content types + */ + search?: { + contentTypes: { + [key: string]: boolean + } + } /** * @description Custom flags */ diff --git a/packages/ripple-tide-event/package.json b/packages/ripple-tide-event/package.json index 7ba48a3098..603d570877 100644 --- a/packages/ripple-tide-event/package.json +++ b/packages/ripple-tide-event/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/ripple-tide-event", "description": "Ripple mappings and components for Tide Event Content type", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "main": "./nuxt.config.ts", "repository": "https://github.com/dpc-sdp/ripple-framework", diff --git a/packages/ripple-tide-grant/components/global/TideGrantSearchResult.vue b/packages/ripple-tide-grant/components/global/TideGrantSearchResult.vue index 8bedda143d..5fa75fe2ae 100644 --- a/packages/ripple-tide-grant/components/global/TideGrantSearchResult.vue +++ b/packages/ripple-tide-grant/components/global/TideGrantSearchResult.vue @@ -29,8 +29,8 @@ const amount = computed(() => { return null }) -const isOnGoing = computed(() => - getSearchResultValue(props.result, 'field_node_on_going') +const isOnGoing = computed( + () => getSearchResultValue(props.result, 'field_node_on_going') ?? false ) const dateFrom = computed(() => diff --git a/packages/ripple-tide-grant/mapping/index.ts b/packages/ripple-tide-grant/mapping/index.ts index 618a39b7de..c97809b310 100644 --- a/packages/ripple-tide-grant/mapping/index.ts +++ b/packages/ripple-tide-grant/mapping/index.ts @@ -1,10 +1,9 @@ -import mime from 'mime-types' import { getBodyFromField, getField, getImageFromField, getLinkFromField, - humanizeFilesize + getDocumentFromField } from '@dpc-sdp/ripple-tide-api' import { tidePageBaseMapping, @@ -89,13 +88,9 @@ const tideGrantModule: IRplTideModuleMapping = { })) }, documents: (src: string) => - getField(src, 'field_node_documents').map((doc: any) => ({ - name: doc.name, - url: doc.field_media_file.url || doc.field_media_file.uri, - extension: mime.extension(doc.field_media_file.filemime), - size: humanizeFilesize(doc.field_media_file.filesize), - id: doc.id - })), + getField(src, 'field_node_documents').map((doc: any) => + getDocumentFromField(doc) + ), sidebarComponents: ['RplSocialShare'] }, includes: [ diff --git a/packages/ripple-tide-grant/package.json b/packages/ripple-tide-grant/package.json index a5813ae04b..699d90b55a 100644 --- a/packages/ripple-tide-grant/package.json +++ b/packages/ripple-tide-grant/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/ripple-tide-grant", "description": "Ripple mappings and components for Tide Grant Content type", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "repository": "https://github.com/dpc-sdp/ripple-framework", "main": "./nuxt.config.ts", diff --git a/packages/ripple-tide-landing-page/components/global/TideLandingPage/Accordion.vue b/packages/ripple-tide-landing-page/components/global/TideLandingPage/Accordion.vue index 88b5740b31..f360b40153 100644 --- a/packages/ripple-tide-landing-page/components/global/TideLandingPage/Accordion.vue +++ b/packages/ripple-tide-landing-page/components/global/TideLandingPage/Accordion.vue @@ -5,6 +5,9 @@ defineProps<{ description?: string numbered: boolean items: Array + hasSidebar?: boolean + hasTitle?: boolean + pageBackground?: string }>() diff --git a/packages/ripple-tide-landing-page/components/global/TideLandingPage/ContentCollection.vue b/packages/ripple-tide-landing-page/components/global/TideLandingPage/ContentCollection.vue index 41fd1dac2e..bdfe1653ee 100644 --- a/packages/ripple-tide-landing-page/components/global/TideLandingPage/ContentCollection.vue +++ b/packages/ripple-tide-landing-page/components/global/TideLandingPage/ContentCollection.vue @@ -55,6 +55,7 @@ import { IContentCollectionSort } from '../../../mapping/components/content-collection/content-collection-mapping' import type { IRplFeatureFlags } from '@dpc-sdp/ripple-tide-api/types' +import { stripMediaBaseUrl } from '@dpc-sdp/ripple-tide-api/utils' const { public: config } = useRuntimeConfig() @@ -88,7 +89,7 @@ const cardClasses = computed(() => ) const searchResultsMappingFn = (item): any => { - const { $app_origin } = useNuxtApp() + const { $app_origin, $config } = useNuxtApp() const rawUpdated = item.changed?.raw?.[0] const rawImage = item.field_media_image_absolute_path?.raw?.[0] @@ -100,7 +101,7 @@ const searchResultsMappingFn = (item): any => { url: stripSiteId(item.url?.raw?.[0], $app_origin || ''), image: props.display.style === 'thumbnail' && rawImage - ? { src: rawImage } + ? { src: stripMediaBaseUrl(rawImage, $config.public?.tide?.baseUrl) } : null, updated: rawUpdated ? formatDate(rawUpdated) : '', type: item.type?.raw?.[0] diff --git a/packages/ripple-tide-landing-page/components/global/TideLandingPage/WebForm.vue b/packages/ripple-tide-landing-page/components/global/TideLandingPage/WebForm.vue index cf27d87cdb..3b3f5a37da 100644 --- a/packages/ripple-tide-landing-page/components/global/TideLandingPage/WebForm.vue +++ b/packages/ripple-tide-landing-page/components/global/TideLandingPage/WebForm.vue @@ -41,6 +41,41 @@ watch( ) const submitted = computed(() => submissionState.value.status === 'success') + +import { FormKitPlugin, FormKitTypeDefinition } from '@formkit/core' +import { + createRplFormInput, + defaultRplFormInputProps, + inputLibrary, + rplFeatures +} from '@dpc-sdp/ripple-ui-forms' + +const appConfig = useAppConfig()?.ripple as { customInputs: any } +const customInputDefs = appConfig.customInputs || {} + +const customInputs: FormKitPlugin = () => {} +customInputs.library = (node: any) => { + Object.values(customInputDefs).forEach((item: any) => { + if (node.props.type === item.id) { + const def: FormKitTypeDefinition = { + schema: createRplFormInput({ + $cmp: item.id, + props: { + ...defaultRplFormInputProps, + type: item.type + } + }), + library: { + [item.id]: resolveComponent(item.id), + ...inputLibrary + }, + ...item.formkitDefProps, + features: rplFeatures + } + return node.define(def) + } + }) +} + + diff --git a/packages/ripple-tide-search/components/TideSearchResultTableHeading.vue b/packages/ripple-tide-search/components/TideSearchResultTableHeading.vue new file mode 100644 index 0000000000..643fc7d4e0 --- /dev/null +++ b/packages/ripple-tide-search/components/TideSearchResultTableHeading.vue @@ -0,0 +1,16 @@ + + + + diff --git a/packages/ripple-tide-search/components/global/TideCustomCollection.vue b/packages/ripple-tide-search/components/global/TideCustomCollection.vue index fa36a418ed..0d72b50e1a 100644 --- a/packages/ripple-tide-search/components/global/TideCustomCollection.vue +++ b/packages/ripple-tide-search/components/global/TideCustomCollection.vue @@ -286,7 +286,12 @@ onMapResultsHook.value = () => { const hookFnName = props.mapConfig?.onResultsHook const fns: Record< string, - (map: any, results: any, locationQuery: any) => Promise + ( + map: any, + results: any, + locationQuery: any, + mapDeadSpace?: any + ) => Promise > = appConfig?.ripple?.search?.mapResultHooks || {} if (!hookFnName) { @@ -301,7 +306,12 @@ onMapResultsHook.value = () => { ) } - hookFn(rplMapRef.value, mapResults.value, locationOrGeolocation.value) + hookFn( + rplMapRef.value, + mapResults.value, + locationOrGeolocation.value, + deadSpace.value + ) } const resultsContainer = computed( @@ -321,6 +331,8 @@ const emitSearchEvent = (event) => { } const handleSearchSubmit = (event) => { + geolocationError.value = null + if (props.userFilters && props.userFilters.length) { cachedSubmitEvent.value = event // Submitting the search term should also 'apply' the filters, but the filters live in a seperate form. @@ -368,6 +380,7 @@ const handleFilterReset = (event: rplEventPayload) => { ) locationQuery.value = null + geolocationError.value = null resetSearch() resetFilters() submitSearch() @@ -552,6 +565,7 @@ const locationOrGeolocation = computed(() => { v-bind="locationQueryConfig?.props" :label="searchListingConfig.labels?.submit" :placeholder="searchListingConfig.labels?.placeholder" + :variant="reverseFields ? 'reverse' : 'default'" :inputValue="locationQuery" :resultsloaded="mapFeatures.length > 0" :isGettingLocation="isGettingLocation" diff --git a/packages/ripple-tide-search/components/global/TideSearchAddressLookup.vue b/packages/ripple-tide-search/components/global/TideSearchAddressLookup.vue index 627fc531fe..7eba28ed56 100644 --- a/packages/ripple-tide-search/components/global/TideSearchAddressLookup.vue +++ b/packages/ripple-tide-search/components/global/TideSearchAddressLookup.vue @@ -13,9 +13,9 @@ :showNoResults="true" :maxSuggestionsDisplayed="8" :placeholder="placeholder" - :getOptionId="(itm:any) => itm?.id || itm?.name" - :getSuggestionVal="(itm:any) => itm?.name || ''" - :getOptionLabel="(itm:any) => itm?.name || ''" + :getOptionId="(itm: any) => itm?.id || itm?.name" + :getSuggestionVal="(itm: any) => itm?.name || ''" + :getOptionLabel="(itm: any) => itm?.name || ''" :isBusy="isGettingLocation" :isFreeText="false" :submitOnClear="true" @@ -43,25 +43,39 @@ diff --git a/packages/ripple-tide-search/composables/useAppConfigFunction.ts b/packages/ripple-tide-search/composables/useAppConfigFunction.ts new file mode 100644 index 0000000000..973f56addf --- /dev/null +++ b/packages/ripple-tide-search/composables/useAppConfigFunction.ts @@ -0,0 +1,16 @@ + + +export default ( + fnName: string, configBucket: string +) => { + const appConfig = useAppConfig() + const fn = appConfig?.ripple?.search?.[configBucket]?.[fnName] + + if (typeof fn !== 'function') { + throw new Error( + `Search listing: No matching function called "${fnName}" in config bucket "${configBucket}"` + ) + } + + return fn +} diff --git a/packages/ripple-tide-search/composables/useLegacySuggestions.ts b/packages/ripple-tide-search/composables/useLegacySuggestions.ts new file mode 100644 index 0000000000..6e199883f7 --- /dev/null +++ b/packages/ripple-tide-search/composables/useLegacySuggestions.ts @@ -0,0 +1,71 @@ +export default async ( + query: string, + suggestionsIndex: string = 'vic-postcode-localities', + suggestionsKey: string = 'name', + mapResultsFnName?: string +) => { + const searchUrl = `/api/tide/app-search/${suggestionsIndex}/elasticsearch/_search` + const queryDSL = { + query: { + bool: { + should: [ + { + match: { + [suggestionsKey]: { + query, + operator: 'and' + } + } + }, + { + prefix: { + [suggestionsKey]: { + value: query, + case_insensitive: true + } + } + }, + { + term: { + postcode: { + value: query + } + } + } + ] + } + } + } + + const response = await $fetch(searchUrl, { + method: 'POST', + body: { + ...queryDSL, + size: 20 + } + }) + + let mappingFn = (itm: any) => { + const center = getSingleResultValue(itm._source.center)?.split(',') + + return { + id: itm._id, + name: getSingleResultValue(itm._source[suggestionsKey]), + postcode: getSingleResultValue(itm._source.postcode), + bbox: itm._source.bbox, + center: center?.length === 2 ? [center[1], center[0]] : undefined + } + } + + // If no transform function is defined, return an empty array + if (mapResultsFnName) { + mappingFn = useAppConfigFunction( + mapResultsFnName, + 'locationSuggestionMappingFunctions' + ) + } + + if (response && response.hits.total.value > 0) { + return response.hits.hits.map(mappingFn) + } +} diff --git a/packages/ripple-tide-search/composables/useSearchResult.ts b/packages/ripple-tide-search/composables/useSearchResult.ts index 9ae64f2418..5f978100f2 100644 --- a/packages/ripple-tide-search/composables/useSearchResult.ts +++ b/packages/ripple-tide-search/composables/useSearchResult.ts @@ -1,14 +1,21 @@ import { computed } from 'vue' import { getSearchResultValue, truncateText } from '#imports' +import { stripMediaBaseUrl } from '@dpc-sdp/ripple-tide-api/utils' interface ResultOptions { summaryMaxLength: number | null } export default (result, options: ResultOptions = { summaryMaxLength: 150 }) => { - const { $app_origin } = useNuxtApp() + const { $app_origin, $config } = useNuxtApp() const title = computed(() => getSearchResultValue(result, 'title')) const url = computed(() => { + const externalURL = getSearchResultValue(result, 'field_redirect_website') + + if (externalURL) { + return externalURL + } + return stripSiteId(getSearchResultValue(result, 'url'), $app_origin || '') }) const updated = computed(() => { @@ -46,7 +53,7 @@ export default (result, options: ResultOptions = { summaryMaxLength: 150 }) => { if (src) { return { - src, + src: stripMediaBaseUrl(src, $config.public?.tide?.baseUrl), alt: '' } } diff --git a/packages/ripple-tide-search/package.json b/packages/ripple-tide-search/package.json index f15aa5572a..d4aa060aef 100644 --- a/packages/ripple-tide-search/package.json +++ b/packages/ripple-tide-search/package.json @@ -1,7 +1,7 @@ { "name": "@dpc-sdp/ripple-tide-search", "description": "Ripple search UI and services for connecting to Tide search", - "version": "2.11.0", + "version": "2.14.0", "license": "Apache-2.0", "repository": "https://github.com/dpc-sdp/ripple-framework", "main": "./nuxt.config.ts", diff --git a/packages/ripple-tide-search/pages/search.vue b/packages/ripple-tide-search/pages/search.vue index e3749fa4f3..896c15a959 100644 --- a/packages/ripple-tide-search/pages/search.vue +++ b/packages/ripple-tide-search/pages/search.vue @@ -1,9 +1,35 @@ @@ -42,7 +45,8 @@ const onClick = () => {
- { (opens in a new window) - +
(), { + name: undefined, + url: undefined, extension: undefined, size: undefined, updated: undefined, - caption: undefined + caption: undefined, + iconSize: 'l' }) const emit = defineEmits<{ @@ -47,11 +52,16 @@ const hasInfo = computed(() => props.extension || props.size || props.updated)