From 9cf6ef4f45b435e9eb298a2b8b421d263912da96 Mon Sep 17 00:00:00 2001 From: Tim Yao <31641325+tim-yao@users.noreply.github.com> Date: Mon, 14 Oct 2019 09:51:03 +1100 Subject: [PATCH] [SDPA-3306] add mapping example (#555) * [SDPA-3306] Added example for mapping with minor fix * Updated search code examples to make them working --- README.md | 4 +- examples/basic-examples/README.md | 4 + .../e2e/integration/custom/mapping.feature | 15 ++ .../custom/mapping/step_definition.js | 15 ++ .../example-override-mapping/README.md | 7 + .../mapping-filters.js | 40 ++++++ .../example-override-mapping/tide.config.js | 42 ++++++ .../tide/modules/example-search/README.md | 12 +- .../example-search/components/Search.vue | 125 ++++++++-------- .../components/SearchSimple.vue | 97 +++++++++++++ .../components/formdata-simple.js | 25 ++++ .../example-search/components/formdata.js | 116 +++++++++++++++ .../tide/modules/example-search/formdata.js | 135 ------------------ .../tide/modules/example-search/module.js | 5 + examples/basic-examples/tide/tide.config.js | 1 + .../test/e2e/integration/smoke/ripple.feature | 4 +- packages/ripple-nuxt-tide/lib/core/mapping.js | 7 +- 17 files changed, 448 insertions(+), 206 deletions(-) create mode 100644 examples/basic-examples/test/e2e/integration/custom/mapping.feature create mode 100644 examples/basic-examples/test/e2e/integration/custom/mapping/step_definition.js create mode 100644 examples/basic-examples/tide/modules/example-override-mapping/README.md create mode 100644 examples/basic-examples/tide/modules/example-override-mapping/mapping-filters.js create mode 100644 examples/basic-examples/tide/modules/example-override-mapping/tide.config.js create mode 100644 examples/basic-examples/tide/modules/example-search/components/SearchSimple.vue create mode 100644 examples/basic-examples/tide/modules/example-search/components/formdata-simple.js create mode 100644 examples/basic-examples/tide/modules/example-search/components/formdata.js delete mode 100644 examples/basic-examples/tide/modules/example-search/formdata.js diff --git a/README.md b/README.md index 6de91526c..046480ce2 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ * [Built With](#built-with) * [Usage](#usage) * [In a Nuxt application](#in-a-Nuxt-application) - * [In a Vue project](#in-a-vue-project) + * [In a Vue project](#in-a-vuejs-projectnot-nuxtjs) * [Contributing](#contributing) * [Getting Started](#getting-started) * [Requirements](#requirements) @@ -114,7 +114,7 @@ You can optionally pass options to `@dpc-sdp/ripple-nuxt-ui` by adding the `ripp Ripple components are published individually as npm modules and can be imported in any Vue project. -Check out our [Vue app example](https://github.com/dpc-sdp/ripple/tree/develop/examples/basic-examples). +Check out our [Vue app example](https://github.com/dpc-sdp/ripple/tree/develop/examples/vue-example-app). ## Contributing diff --git a/examples/basic-examples/README.md b/examples/basic-examples/README.md index e2f5b29d2..158d9348d 100644 --- a/examples/basic-examples/README.md +++ b/examples/basic-examples/README.md @@ -28,6 +28,10 @@ Provide a example for overriding the Ripple components in Tide landing page which is dynamically loaded. +- [Override component mapping](tide/modules/example-override-mapping/) + + Provide a example for overriding the components mapping. + - [Custom search page](tide/modules/example-search/) Provide a example for adding a custom search page. diff --git a/examples/basic-examples/test/e2e/integration/custom/mapping.feature b/examples/basic-examples/test/e2e/integration/custom/mapping.feature new file mode 100644 index 000000000..ecac71bc3 --- /dev/null +++ b/examples/basic-examples/test/e2e/integration/custom/mapping.feature @@ -0,0 +1,15 @@ +Feature: Custom mapping + + Custom mapping should work + + Background: + Given I visit the page "/" + + Scenario: Example for overriding mapping with custom filter + And the example filter is applied + + Scenario: Example for adding async filter + And the example async filter is applied + + Scenario: Example for overriding core filter + And the core filter is overrode diff --git a/examples/basic-examples/test/e2e/integration/custom/mapping/step_definition.js b/examples/basic-examples/test/e2e/integration/custom/mapping/step_definition.js new file mode 100644 index 000000000..e43262db0 --- /dev/null +++ b/examples/basic-examples/test/e2e/integration/custom/mapping/step_definition.js @@ -0,0 +1,15 @@ +/* global cy */ + +import { Then } from 'cypress-cucumber-preprocessor/steps' + +Then(`the example filter is applied`, () => { + cy.get('.rpl-hero-banner__title span').should('contain', '[demo]') +}) + +Then(`the example async filter is applied`, () => { + cy.get('.rpl-hero-banner__description').should('contain', '[demo]') +}) + +Then(`the core filter is overrode`, () => { + cy.get('.rpl-hero-banner__link-list-item .rpl-text-label span span').should('contain', '[demo]') +}) diff --git a/examples/basic-examples/tide/modules/example-override-mapping/README.md b/examples/basic-examples/tide/modules/example-override-mapping/README.md new file mode 100644 index 000000000..5f3c01990 --- /dev/null +++ b/examples/basic-examples/tide/modules/example-override-mapping/README.md @@ -0,0 +1,7 @@ +# Ripple with custom mapping example + +The mapping config is for mapping Tide data to Frontend Vue component(Not have to be Ripple components). + +This is a example for overriding the Ripple default component mapping. + +In this module, we are override the `heroBanner` mapping with custom filters. diff --git a/examples/basic-examples/tide/modules/example-override-mapping/mapping-filters.js b/examples/basic-examples/tide/modules/example-override-mapping/mapping-filters.js new file mode 100644 index 000000000..19a165e91 --- /dev/null +++ b/examples/basic-examples/tide/modules/example-override-mapping/mapping-filters.js @@ -0,0 +1,40 @@ +// Filters for adding extra process on a mapping value + +// Create more filters if need. +module.exports = { + + // Add your own filter, must with your own name prefix to avoid name conflict. + eomPageTitle: (pageTitle) => { + return `[demo] ${pageTitle}` + }, + + // An async filter example. + eomIntroText: async (introText) => { + const promise = new Promise((resolve, reject) => { + // Do some process + setTimeout(function () { + const processed = `[demo] ${introText}` + resolve(processed) + }, 300) + }) + return promise + }, + + // We can override core filter by using same core filter name. + // Ripple v1.3.x required for this feature. + // ! Be careful, this way is hard to maintain. + // Use your own filter like above is the safer way. + paragraphKeyJourneyLinks: function (fieldParagraphLinks) { + if (typeof fieldParagraphLinks !== 'undefined' && fieldParagraphLinks !== null) { + let rtn = [] + fieldParagraphLinks.forEach(item => { + rtn.push({ + url: item.url || item.uri, + text: `[demo] ${item.title}` + }) + }) + return rtn + } + } + +} diff --git a/examples/basic-examples/tide/modules/example-override-mapping/tide.config.js b/examples/basic-examples/tide/modules/example-override-mapping/tide.config.js new file mode 100644 index 000000000..0989cd9cf --- /dev/null +++ b/examples/basic-examples/tide/modules/example-override-mapping/tide.config.js @@ -0,0 +1,42 @@ +// Grid columns setting for cards. + +module.exports = { + + mapping: { + tideField: { + + // A example for overriding default `heroBanner` mapping in https://github.com/dpc-sdp/ripple/blob/v1.2.1/packages/ripple-nuxt-tide/lib/config/tide.config.js#L33 + // You can do: + // - Map to your own component. + // - Use your own filters to process the Tide field data. + 'heroBanner': { + component: 'rpl-hero-banner', + props: { + title: { + field: 'pageTitle', + // Demo of a custom filter here. + filters: ['eomPageTitle'] + }, + introText: { + field: 'introText', + // Demo of another custom filter here. + filters: ['eomIntroText'] + }, + linkHeading: ['keyJourneys', 'field_paragraph_title'], + links: { + field: ['keyJourneys', 'field_paragraph_links'], + filters: ['paragraphKeyJourneyLinks'] + }, + moreLink: { + field: ['keyJourneys', 'field_paragraph_cta'], + filters: ['paragraphCta'] + }, + theme: 'theme', + showLinks: 'showLinks', + logo: 'logo', + backgroundGraphic: 'backgroundGraphic' + } + } + } + } +} diff --git a/examples/basic-examples/tide/modules/example-search/README.md b/examples/basic-examples/tide/modules/example-search/README.md index 0f392c431..7d899a7a1 100644 --- a/examples/basic-examples/tide/modules/example-search/README.md +++ b/examples/basic-examples/tide/modules/example-search/README.md @@ -4,6 +4,14 @@ This example shows how to add a custom search page. Three files required: -- `Search.vue`: Used for providing search form and search results. -- `formdata.js`: Used for providing search form configuration. +- Vue component e.g `Search.vue`: Used for providing search form and search results. +- Form config e.g `formdata.js`: Used for providing search form configuration. - `module.js`: Used for providing search page route. + +## Example search simple + +It adds a very basic search page `/example-search-simple` has basic elements only. + +## Example search + +It adds a search page `/example-search`. diff --git a/examples/basic-examples/tide/modules/example-search/components/Search.vue b/examples/basic-examples/tide/modules/example-search/components/Search.vue index 549521e06..4881fdbf6 100644 --- a/examples/basic-examples/tide/modules/example-search/components/Search.vue +++ b/examples/basic-examples/tide/modules/example-search/components/Search.vue @@ -1,50 +1,45 @@ - - diff --git a/examples/basic-examples/tide/modules/example-search/components/SearchSimple.vue b/examples/basic-examples/tide/modules/example-search/components/SearchSimple.vue new file mode 100644 index 000000000..506beb5cc --- /dev/null +++ b/examples/basic-examples/tide/modules/example-search/components/SearchSimple.vue @@ -0,0 +1,97 @@ + + + diff --git a/examples/basic-examples/tide/modules/example-search/components/formdata-simple.js b/examples/basic-examples/tide/modules/example-search/components/formdata-simple.js new file mode 100644 index 000000000..17a044cf9 --- /dev/null +++ b/examples/basic-examples/tide/modules/example-search/components/formdata-simple.js @@ -0,0 +1,25 @@ +export default { + getFormData: async setFilterOptions => { + return { + title: 'Example search simple', + subtitle: 'A simple search form, no filters, default results style. Search "event".', + searchPlaceholder: 'Start typing...', + theme: 'light', + expandFilters: true, + allowBlank: true, + filterForm: { + tideId: 'tide_search_form', + tag: 'rpl-fieldset', + model: { + }, + schema: { + }, + formOptions: { + validateAfterLoad: false, + validateAfterChanged: false + }, + formState: {} + } + } + } +} diff --git a/examples/basic-examples/tide/modules/example-search/components/formdata.js b/examples/basic-examples/tide/modules/example-search/components/formdata.js new file mode 100644 index 000000000..d19d97d72 --- /dev/null +++ b/examples/basic-examples/tide/modules/example-search/components/formdata.js @@ -0,0 +1,116 @@ +export default { + getFormData: async setFilterOptions => { + const eventCategoryValues = await setFilterOptions({ + fieldName: 'field_event_category_name' + }) + const eventNameValues = await setFilterOptions({ + fieldName: 'field_event_details_event_requirements_name' + }) + return { + title: 'Example search page', + subtitle: 'A search form with filters, card result style. Search "event".', + searchPlaceholder: 'Start typing...', + theme: 'light', + allowBlank: true, + filterForm: { + tideId: 'tide_search_form', + tag: 'rpl-fieldset', + model: { + // Multi-value fields should always be an array. + field_event_category_name: [], + field_event_date_end_value: '', + location: '', + field_event_details_event_requirements_name: [] + }, + schema: { + groups: [ + { + fields: [ + { + type: 'rplchecklist', + styleClasses: ['form-group--col-two'], + label: 'Select an event category', + model: 'field_event_category_name', + values: eventCategoryValues, + placeholder: 'Select a topic', + // TODO: Update 'filter' key to 'query' to make purpose clearer. + filter: { + type: 'term', + operator: '' + } + }, + { + type: 'input', + inputType: 'text', + maxlength: 50, + styleClasses: ['form-group--col-two'], + label: 'Location', + model: 'location', + placeholder: 'Start typing suburb or postcode...', + filter: { + type: 'multiMatch', + operator: '', + fields: [ + 'field_event_details_event_locality', + 'field_event_details_event_postal_code' + ] + } + } + ] + }, + { + fields: [ + { + type: 'rpldatepicker', + ranged: false, + styleClasses: ['form-group--col-two'], + label: 'Select an event date', + model: 'field_event_date_end_value', + placeholder: 'DD/MM/YYYY', + filter: { + type: 'date', + operator: 'gte' + } + }, + { + type: 'rplchecklist', + label: 'Event requirements', + styleClasses: ['form-group--col-two'], + model: 'field_event_details_event_requirements_name', + // TODO: There are no values for this field how can we programmactically hide a field in this instance. + values: eventNameValues, + placeholder: 'Please select', + filter: { + type: 'term', + operator: '' + } + } + ] + }, + { + fields: [ + { + type: 'rplsubmitloader', + buttonText: 'Apply change', + loading: false, + autoUpdate: true, + styleClasses: ['form-group--inline'] + }, + { + type: 'rplclearform', + buttonText: 'Clear search filters', + styleClasses: ['form-group--inline'] + } + ] + } + ] + }, + formOptions: { + validateAfterLoad: false, + validateAfterChanged: false + }, + formState: {} + } + } + } +} diff --git a/examples/basic-examples/tide/modules/example-search/formdata.js b/examples/basic-examples/tide/modules/example-search/formdata.js deleted file mode 100644 index 405bb4770..000000000 --- a/examples/basic-examples/tide/modules/example-search/formdata.js +++ /dev/null @@ -1,135 +0,0 @@ -export default { - getFormData: async setFilterOptions => { - const fieldProfileCategoryValues = await setFilterOptions({ - fieldName: 'field_profile_category_name' - }) - const fieldYearValues = await setFilterOptions({ - fieldName: 'field_year' - }) - const fieldProfileExpertiseNameValues = await setFilterOptions({ - fieldName: 'field_profile_expertise_name' - }) - const fieldLocationNameValues = await setFilterOptions({ - fieldName: 'field_location_name' - }) - - return { - title: 'Victorian Honour Roll of women inductees', - subtitle: 'View our outstanding inductees.', - searchPlaceholder: 'Start typing...', - theme: 'light', - textSearch: false, - expandFilters: true, - allowBlank: true, - filterForm: { - tag: 'rpl-fieldset', - tideId: 'tide_search_form', - model: { - title: '', - field_profile_category_name: [], - field_year: [], - field_profile_expertise_name: [], - field_location_name: [] - }, - schema: { - groups: [ - { - legend: 'View by', - fields: [ - { - type: 'input', - inputType: 'text', - maxlength: 50, - styleClasses: ['form-group--col-two'], - label: 'Honouree name', - model: 'title', - placeholder: 'Start typing first name or surname...', - filter: { - type: 'term' - } - }, - { - type: 'rplchecklist', - label: 'Category', - model: 'field_profile_category_name', - styleClasses: ['form-group--col-two'], - values: fieldProfileCategoryValues, - placeholder: 'Select a category', - filter: { - type: 'term', - operator: '' - } - } - ] - }, - { - fields: [ - { - type: 'rplchecklist', - label: 'Induction year', - model: 'field_year', - styleClasses: ['form-group--col-two'], - values: fieldYearValues.filter(year => year.length === 4), // TODO: Temporary fix for invalid response in search index [SDPA-2000] - placeholder: 'Select a year', - filter: { - type: 'term', - operator: '' - } - }, - { - type: 'rplchecklist', - label: 'Field of expertise', - model: 'field_profile_expertise_name', - styleClasses: ['form-group--col-two'], - values: fieldProfileExpertiseNameValues, - placeholder: 'Select field of expertise', - filter: { - type: 'term', - operator: '' - } - } - ] - }, - { - fields: [ - { - type: 'rplchecklist', - label: 'Location', - model: 'field_location_name', - styleClasses: ['form-group--col-two'], - values: fieldLocationNameValues, - placeholder: 'Select a location', - filter: { - type: 'term', - operator: '' - } - } - ] - }, - { - fields: [ - { - type: 'rplsubmitloader', - buttonText: 'Apply change', - loading: false, - autoUpdate: true, - styleClasses: ['form-group--inline'] - }, - { - type: 'rplclearform', - buttonText: 'Clear search filters', - styleClasses: ['form-group--inline'] - } - ] - } - ] - }, - formOptions: { - validateAfterLoad: false, - validateAfterChanged: false - }, - formState: {} - } - } - } -} diff --git a/examples/basic-examples/tide/modules/example-search/module.js b/examples/basic-examples/tide/modules/example-search/module.js index 05ace4fc5..763967793 100644 --- a/examples/basic-examples/tide/modules/example-search/module.js +++ b/examples/basic-examples/tide/modules/example-search/module.js @@ -6,5 +6,10 @@ module.exports = function () { path: '/example-search', component: '~/tide/modules/example-search/components/Search.vue' }) + routes.push({ + name: 'example-search-simple', + path: '/example-search-simple', + component: '~/tide/modules/example-search/components/SearchSimple.vue' + }) }) } diff --git a/examples/basic-examples/tide/tide.config.js b/examples/basic-examples/tide/tide.config.js index 97148823d..9ec71713b 100644 --- a/examples/basic-examples/tide/tide.config.js +++ b/examples/basic-examples/tide/tide.config.js @@ -6,6 +6,7 @@ const tideConfig = { 'example-middleware', 'example-content-type', 'example-override-component', + 'example-override-mapping', 'example-search' ] } diff --git a/examples/vue-example-app/test/e2e/integration/smoke/ripple.feature b/examples/vue-example-app/test/e2e/integration/smoke/ripple.feature index 226f102e0..4b33cfb92 100644 --- a/examples/vue-example-app/test/e2e/integration/smoke/ripple.feature +++ b/examples/vue-example-app/test/e2e/integration/smoke/ripple.feature @@ -1,6 +1,6 @@ -Feature: Custom theme should work +Feature: Ripple components - Custom theme is getting loaded + Ripple components in Vue.js app Background: Given I visit the Vue router path "/" diff --git a/packages/ripple-nuxt-tide/lib/core/mapping.js b/packages/ripple-nuxt-tide/lib/core/mapping.js index 5c3f9273c..003daa66f 100644 --- a/packages/ripple-nuxt-tide/lib/core/mapping.js +++ b/packages/ripple-nuxt-tide/lib/core/mapping.js @@ -1,5 +1,6 @@ import tideDefaultConfig from '../config/tide.config' import defaultFilters from './mapping-filters' +import logger from './logger' const _getValFromFieldArray = Symbol('getValFromFieldArray') const _getValFromFieldOptions = Symbol('getValFromFieldOptions') @@ -90,9 +91,11 @@ export class Mapping { if (configForMergeIn) { for (const type in configForMergeIn) { if (config[type] && typeof configForMergeIn[type] !== 'function') { - // We don't allow function in core to be overriden in extend or custom config. config[type] = Object.assign(config[type], configForMergeIn[type]) - } else if (config[type] === undefined && typeof configForMergeIn[type] === 'function') { + } else if (typeof configForMergeIn[type] === 'function') { + if (config[type] && process.server) { + logger.info('Mapping filter "%s" has been overridden by custom module', type, { label: 'Mapping' }) + } config[type] = configForMergeIn[type] } else { config[type] = Object.assign({}, configForMergeIn[type])