├── .builderrc ├── .gitignore ├── .npmignore ├── README.md ├── example ├── agentAssist.js ├── checklist.js ├── counter.js ├── customer360.js ├── flashCard.js └── taskBoard.js ├── index.js ├── now-cli.json ├── now-ui.json ├── package.json └── src ├── agent-assist-example ├── agent-assist-item │ ├── agentAssistItem.js │ ├── index.js │ ├── styles.scss │ └── view.js ├── agent-assist │ ├── actionHandlers.js │ ├── agentAssist.js │ ├── index.js │ ├── renderSearchResponse.js │ ├── styles.scss │ └── view.js ├── constants.js └── index.js ├── checklist-example ├── constants.js ├── example-checklist-item │ ├── actions.js │ ├── checklist-item.js │ ├── checklist-item.scss │ ├── index.js │ └── view.js ├── example-checklist │ ├── actions.js │ ├── checklist.js │ ├── checklist.scss │ ├── index.js │ └── view.js └── index.js ├── counter-example ├── example-counter │ ├── counter.js │ ├── counter.scss │ ├── index.js │ └── view.js └── index.js ├── customer-360-example ├── actionHandlers.js ├── constants.js ├── customer360.js ├── getCallerData.js ├── getHttpEffect.js ├── index.js ├── styles.scss └── view.js ├── flash-card-example ├── constants.js ├── example-card-list │ ├── card-list.js │ ├── card-list.scss │ ├── index.js │ └── view.js ├── example-flash-card │ ├── flash-card.js │ ├── flash-card.scss │ ├── index.js │ └── view.js └── index.js ├── index.js └── task-board-example ├── behaviors └── dragDropBehaviors.js ├── constants.js ├── example-card ├── card.js ├── card.scss └── index.js ├── example-lane ├── index.js ├── lane.js ├── lane.scss └── view.js ├── example-task-board ├── actions.js ├── index.js ├── task-board.js ├── task-board.scss └── view.js └── index.js /.builderrc: -------------------------------------------------------------------------------- 1 | --- 2 | archetypes: 3 | - "@servicenow/cli-archetype" 4 | - "@servicenow/cli-component-archetype" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .DS_Store 3 | node_modules 4 | lib 5 | dist 6 | target 7 | node 8 | node_tmp 9 | npm-debug.log 10 | *.iml 11 | *.tgz 12 | coverage 13 | .now-cli 14 | .babelrc 15 | /clover.xml 16 | /coverage-final.json 17 | /lcov-report/ 18 | /lcov.info 19 | /dist/ 20 | /lib/ 21 | /target/ 22 | package-lock.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.iml 4 | *.tgz 5 | .*.haste_cache.* 6 | .DS_Store 7 | .idea 8 | .babelrc 9 | .eslintrc 10 | npm-debug.log 11 | !lib 12 | !dist 13 | __tests__ 14 | __mocks__ 15 | target 16 | coverage 17 | node_modules 18 | node 19 | node_tmp 20 | /.now-cli/ 21 | /lcov-report/ 22 | !/now-cli.* 23 | !/dist/ 24 | !/lib/ 25 | !/target/ 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Component built using Now Experience UI Framework 2 | ## Setting up 3 | 1. Install Now Experience CLI globally, if not done already 4 | 5 | ``` 6 | npm install --global @servicenow/cli 7 | ``` 8 | 9 | 2. Open the local repo folder 10 | 11 | ``` 12 | cd now-experience-component-examples 13 | ``` 14 | 15 | 3. Install required dependencies 16 | 17 | ``` 18 | npm install 19 | ``` 20 | 21 | 4. Add your instance host address to `development.proxy.origin` in the `now-cli.json` and authenticate using `now-cli login` command. 22 | 23 | ## Run examples 24 | 25 | 1. Counter Example 26 | 27 | ``` 28 | npm run start:counterExample 29 | ``` 30 | 31 | 2. Checklist Example 32 | 33 | ``` 34 | npm run start:checklistExample 35 | ``` 36 | 37 | 3. Agent Assist Example 38 | 39 | ``` 40 | npm run start:agentAssistExample 41 | ``` 42 | 43 | 4. Customer 360 Example 44 | 45 | ``` 46 | npm run start:customer360Example 47 | ``` 48 | 49 | 5. Flash Card Example 50 | 51 | ``` 52 | npm run start:flashCardExample 53 | ``` 54 | 55 | 6. Task Board Example 56 | 57 | ``` 58 | npm run start:taskBoardExample 59 | ``` 60 | 61 | > To know more about `now-cli`, see [Now Experience CLI](https://developer.servicenow.com/dev.do#!/guide/orlando/now-experience/cli/cli) 62 | 63 | > To know more about NOW UI Framework, see [Now® Experience UI Framework](https://developer.servicenow.com/dev.do#!/guides/orlando/now-experience/ui-framework/getting-started/introduction) 64 | -------------------------------------------------------------------------------- /example/agentAssist.js: -------------------------------------------------------------------------------- 1 | import '../src/agent-assist-example'; 2 | 3 | const el = document.createElement('DIV'); 4 | document.body.appendChild(el); 5 | 6 | el.innerHTML = ` 7 |
8 | 9 |
10 | `; 11 | -------------------------------------------------------------------------------- /example/checklist.js: -------------------------------------------------------------------------------- 1 | import '../src/checklist-example'; 2 | 3 | const el = document.createElement('DIV'); 4 | document.body.appendChild(el); 5 | 6 | el.innerHTML = ` 7 | 8 | `; 9 | -------------------------------------------------------------------------------- /example/counter.js: -------------------------------------------------------------------------------- 1 | import '../src/counter-example'; 2 | 3 | const el = document.createElement('DIV'); 4 | document.body.appendChild(el); 5 | 6 | el.innerHTML = ` 7 | 8 | `; 9 | -------------------------------------------------------------------------------- /example/customer360.js: -------------------------------------------------------------------------------- 1 | import '../src/customer-360-example'; 2 | 3 | const el = document.createElement('DIV'); 4 | document.body.appendChild(el); 5 | 6 | el.innerHTML = ` 7 |
8 | 9 |
10 | `; 11 | -------------------------------------------------------------------------------- /example/flashCard.js: -------------------------------------------------------------------------------- 1 | import '../src/flash-card-example'; 2 | 3 | const el = document.createElement('DIV'); 4 | document.body.appendChild(el); 5 | 6 | el.innerHTML = ` 7 | 8 | `; 9 | -------------------------------------------------------------------------------- /example/taskBoard.js: -------------------------------------------------------------------------------- 1 | import '../src/task-board-example'; 2 | 3 | const el = document.createElement('DIV'); 4 | document.body.appendChild(el); 5 | 6 | el.innerHTML = ` 7 | 8 | `; 9 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /now-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "proxy": { 4 | "origin": "https://.com", 5 | "port": 3000, 6 | "proxies": ["/api"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /now-ui.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "example-agent-assist" : { 4 | "innerComponents": [ 5 | "now-icon", 6 | "now-loader", 7 | "now-heading" 8 | ] 9 | }, 10 | "example-customer-360": { 11 | "innerComponents": [ 12 | "now-avatar", 13 | "now-icon", 14 | "now-label-value-stacked", 15 | "now-label-value-inline", 16 | "now-heading" 17 | ], 18 | "properties": [ 19 | { 20 | "name": "table", 21 | "label": "table", 22 | "description": "Name of the table to pick caller from", 23 | "readOnly": false, 24 | "fieldType": "table_name", 25 | "required": true, 26 | "defaultValue": "incident", 27 | "typeMetadata": {} 28 | }, 29 | { 30 | "name": "sysId", 31 | "label": "sysId", 32 | "description": "The unique identifier for the record", 33 | "readOnly": false, 34 | "fieldType": "string", 35 | "required": true, 36 | "defaultValue": "85071a1347c12200e0ef563dbb9a71c1", 37 | "typeMetadata": {} 38 | } 39 | ] 40 | }, 41 | "example-checklist" : { 42 | "uiBuilder": { 43 | "label": "Checklist", 44 | "icon": "list-fill", 45 | "description": "Logged in users can manage their tasks", 46 | "category": "primitives", 47 | "associatedTypes": [ 48 | "global.core" 49 | ] 50 | }, 51 | "innerComponents": [ 52 | "now-heading", 53 | "now-loader", 54 | "now-button", 55 | "now-toggle", 56 | "example-checklist-item", 57 | "now-label-value-inline", 58 | "now-button-iconic" 59 | ] 60 | } 61 | }, 62 | "scopeName": "x_now_components" 63 | } 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@servicenow/now-exeperience-sample-components", 3 | "version": "0.0.1", 4 | "private": false, 5 | "description": "Custom elements to show sample Customer360 , To-Do List & Agent Assist", 6 | "keywords": [ 7 | "ServiceNow", 8 | "Now Experience UI Component", 9 | "@servicenow/now-exeperience-sample-components" 10 | ], 11 | "readme": "./README.md", 12 | "engines": { 13 | "node": ">=8.6.0", 14 | "npm": ">=5.3.0" 15 | }, 16 | "module": "src/index.js", 17 | "scripts": { 18 | "start:agentAssistExample": "now-cli develop --open --entry=./agentAssist.js", 19 | "start:counterExample": "now-cli develop --open --entry=./counter.js", 20 | "start:checklistExample": "now-cli develop --open --entry=./checklist.js", 21 | "start:customer360Example": "now-cli develop --open --entry=./customer360.js", 22 | "start:flashCardExample": "now-cli develop --open --entry=./flashCard.js", 23 | "start:taskBoardExample": "now-cli develop --open --entry=./taskBoard.js" 24 | }, 25 | "dependencies": { 26 | "@servicenow/behavior-rtl": "orlando", 27 | "@servicenow/now-avatar": "orlando", 28 | "@servicenow/now-heading": "orlando", 29 | "@servicenow/now-icon": "orlando", 30 | "@servicenow/now-label-value": "orlando", 31 | "@servicenow/sass-kit": "orlando", 32 | "@servicenow/sass-theme": "orlando", 33 | "@servicenow/now-loader": "orlando", 34 | "@servicenow/now-button": "orlando", 35 | "@servicenow/now-toggle": "orlando", 36 | "@servicenow/ui-core": "orlando", 37 | "@servicenow/ui-effect-http": "orlando", 38 | "@servicenow/ui-renderer-snabbdom": "orlando", 39 | "@servicenow/cli-archetype": "orlando", 40 | "@servicenow/cli-component-archetype": "orlando" 41 | }, 42 | "devDependencies": { 43 | "@servicenow/cli-archetype-dev": "orlando", 44 | "@servicenow/cli-component-archetype-dev": "orlando" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist-item/agentAssistItem.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import view from './view'; 4 | import styles from './styles.scss'; 5 | 6 | createCustomElement('agent-assist-item', { 7 | renderer: {type: snabbdom}, 8 | view, 9 | styles, 10 | properties: { 11 | articleShortDescription: { 12 | default: null 13 | }, 14 | articleBody: { 15 | default: null 16 | } 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist-item/index.js: -------------------------------------------------------------------------------- 1 | import './agentAssistItem'; 2 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist-item/styles.scss: -------------------------------------------------------------------------------- 1 | @import '@servicenow/sass-kit/host'; 2 | 3 | article { 4 | border-radius: $now-global-border-radius--sm; 5 | border: 1px solid RGB($now-color--divider-primary); 6 | margin: $now-global-space--sm 0px; 7 | padding: $now-global-space--md; 8 | header { 9 | display: grid; 10 | grid-template-columns: now-fn-px2rem(30px) auto; 11 | align-items: center; 12 | span { 13 | color: RGB($now-color--text-secondary); 14 | } 15 | now-heading { 16 | margin-top: $now-global-space--md; 17 | grid-column: 1 / span 2; 18 | } 19 | } 20 | summary { 21 | white-space: normal; 22 | margin-top: $now-global-space--xs; 23 | color: RGB($now-color--text-tertiary); 24 | font-size: $now-global-font-size--sm; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist-item/view.js: -------------------------------------------------------------------------------- 1 | import '@servicenow/now-heading'; 2 | 3 | const trimArticleBody = function (articleBody) { 4 | if (articleBody) 5 | return articleBody 6 | .replace(/<([^>]+)>/g, '') 7 | .replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec)) 8 | .substring(0, 150) 9 | .trim(); 10 | 11 | return null; 12 | }; 13 | 14 | export default state => { 15 | const { 16 | properties: {articleShortDescription, articleBody} 17 | } = state; 18 | 19 | return ( 20 |
21 |
22 | 23 | Article 24 | 28 |
29 | {`${trimArticleBody(articleBody)}...`} 30 |
31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist/actionHandlers.js: -------------------------------------------------------------------------------- 1 | import {actionTypes} from '@servicenow/ui-core'; 2 | import {createHttpEffect} from '@servicenow/ui-effect-http'; 3 | import { 4 | KB_KNOWLEDGE_REST_URL, 5 | KB_KNOWLEDGE_FETCH_REQUESTED, 6 | KB_KNOWLEDGE_FETCH_STARTED, 7 | KB_KNOWLEDGE_FETCH_SUCCESS, 8 | KB_KNOWLEDGE_FETCH_FAILED, 9 | SEARCH_REQUESTED, 10 | KB_KNOWLEDGE_TABLE, 11 | NUMBER_OF_RECORDS_FETCH 12 | } from '../constants'; 13 | 14 | const triggerSearch = (fields, {dispatch, updateState}) => { 15 | const searchString = 16 | fields && fields.short_description && fields.short_description.value 17 | ? fields.short_description.value.trim() 18 | : ''; 19 | 20 | if (searchString) { 21 | dispatch(SEARCH_REQUESTED, {searchString}); 22 | } else { 23 | updateState({ 24 | searchString, 25 | result: [] 26 | }); 27 | } 28 | }; 29 | 30 | export default { 31 | [actionTypes.COMPONENT_BOOTSTRAPPED]: ({ 32 | dispatch, 33 | updateState, 34 | properties: {fields} 35 | }) => { 36 | triggerSearch(fields, {dispatch, updateState}); 37 | }, 38 | [actionTypes.COMPONENT_PROPERTY_CHANGED]: ({ 39 | dispatch, 40 | updateState, 41 | action 42 | }) => { 43 | const { 44 | payload: {value: fields} 45 | } = action; 46 | triggerSearch(fields, {dispatch, updateState}); 47 | }, 48 | [SEARCH_REQUESTED]: ({dispatch, updateState, action}) => { 49 | const { 50 | payload: {searchString} 51 | } = action; 52 | const sysparm_query = `short_descriptionLIKE${searchString}^ORtextLIKE${searchString}`; 53 | updateState({searchString}); 54 | dispatch(KB_KNOWLEDGE_FETCH_REQUESTED, { 55 | table: KB_KNOWLEDGE_TABLE, 56 | sysparm_query, 57 | sysparm_limit: NUMBER_OF_RECORDS_FETCH 58 | }); 59 | }, 60 | [KB_KNOWLEDGE_FETCH_REQUESTED]: createHttpEffect(KB_KNOWLEDGE_REST_URL, { 61 | pathParams: ['table'], 62 | queryParams: ['sysparm_query', 'sysparm_limit'], 63 | startActionType: KB_KNOWLEDGE_FETCH_STARTED, 64 | successActionType: KB_KNOWLEDGE_FETCH_SUCCESS, 65 | errorActionType: KB_KNOWLEDGE_FETCH_FAILED 66 | }), 67 | [KB_KNOWLEDGE_FETCH_STARTED]: ({updateState}) => { 68 | updateState({isLoading: true}); 69 | }, 70 | [KB_KNOWLEDGE_FETCH_SUCCESS]: ({action, updateState}) => { 71 | const { 72 | payload: {result = []} 73 | } = action; 74 | updateState({ 75 | isLoading: false, 76 | result 77 | }); 78 | }, 79 | [KB_KNOWLEDGE_FETCH_FAILED]: ({action, updateState}) => { 80 | const { 81 | type, 82 | payload: { 83 | statusText, 84 | data: { 85 | error: {message} 86 | } 87 | } 88 | } = action; 89 | console.error(`[${type}] ${statusText}: ${message}!`); // eslint-disable-line no-console 90 | updateState({isLoading: false, result: []}); 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist/agentAssist.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import view from './view'; 4 | import actionHandlers from './actionHandlers'; 5 | import styles from './styles.scss'; 6 | 7 | createCustomElement('example-agent-assist', { 8 | renderer: {type: snabbdom}, 9 | view, 10 | styles, 11 | initialState: { 12 | searchString: null, 13 | isLoading: false, 14 | result: [] 15 | }, 16 | properties: { 17 | fields: { 18 | default: { 19 | short_description: { 20 | displayValue: 'email', 21 | value: 'email', 22 | visible: true 23 | } 24 | } 25 | } 26 | }, 27 | actionHandlers 28 | }); 29 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist/index.js: -------------------------------------------------------------------------------- 1 | import './agentAssist'; 2 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist/renderSearchResponse.js: -------------------------------------------------------------------------------- 1 | import {Fragment} from '@servicenow/ui-renderer-snabbdom'; 2 | import '@servicenow/now-label-value'; 3 | import '../agent-assist-item'; 4 | import {NO_MATCHES_FOUND, NO_MATCHES_FOUND_MESSAGE} from '../constants'; 5 | 6 | export const renderSearchResponse = result => ( 7 | 8 | {result.length ? ( 9 | result.map(item => ( 10 | 16 | )) 17 | ) : ( 18 | 19 | 20 | 21 | 22 | )} 23 | 24 | ); 25 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist/styles.scss: -------------------------------------------------------------------------------- 1 | @import '@servicenow/sass-kit/host'; 2 | 3 | :host { 4 | display: block; 5 | height: 100%; 6 | } 7 | .now-agent-assist { 8 | display: flex; 9 | flex-direction: column; 10 | max-width: now-fn-px2rem(300px); 11 | height: inherit; 12 | margin: auto; 13 | padding: $now-global-space--lg; 14 | background-color: RGB($now-color--background-primary); 15 | header { 16 | .now-agent-assist-search-input { 17 | width: 100%; 18 | line-height: $now-global-line-height; 19 | font-size: $now-global-font-size--md; 20 | margin-bottom: $now-global-space--lg; 21 | &:focus { 22 | outline-color: RGB($now-color--category-green-3); 23 | } 24 | } 25 | } 26 | main { 27 | overflow: auto; 28 | padding: $now-global-font-size--sm 0px; 29 | .no-response-found { 30 | display: grid; 31 | justify-items: center; 32 | grid-gap: $now-global-space--sm; 33 | padding: $now-global-space--md; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/agent-assist-example/agent-assist/view.js: -------------------------------------------------------------------------------- 1 | import '@servicenow/now-icon'; 2 | import '@servicenow/now-loader'; 3 | import '@servicenow/now-heading'; 4 | import {renderSearchResponse} from './renderSearchResponse'; 5 | import {SEARCH_REQUESTED} from '../constants'; 6 | 7 | export default (state, {dispatch, updateState}) => { 8 | const {isLoading, searchString, result} = state; 9 | 10 | const triggerSearch = ({target: {value}}) => { 11 | const searchValue = value.trim(); 12 | 13 | if (searchValue === searchString) { 14 | return; 15 | } else if (searchValue) { 16 | dispatch(SEARCH_REQUESTED, {searchString: searchValue}); 17 | } else { 18 | updateState({ 19 | searchString: searchValue, 20 | result: [] 21 | }); 22 | } 23 | }; 24 | 25 | return ( 26 |
27 |
28 | 29 | 33 |
34 |
35 | {isLoading ? ( 36 | 37 | ) : ( 38 | renderSearchResponse(result) 39 | )} 40 |
41 |
42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /src/agent-assist-example/constants.js: -------------------------------------------------------------------------------- 1 | export const KB_KNOWLEDGE_REST_URL = '/api/now/table/:table'; 2 | export const SEARCH_REQUESTED = 'SEARCH_REQUESTED'; 3 | export const KB_KNOWLEDGE_FETCH_REQUESTED = 'KB_KNOWLEDGE_FETCH_REQUESTED'; 4 | export const KB_KNOWLEDGE_FETCH_STARTED = 'KB_KNOWLEDGE_FETCH_STARTED'; 5 | export const KB_KNOWLEDGE_FETCH_SUCCESS = 'KB_KNOWLEDGE_FETCH_SUCCESS'; 6 | export const KB_KNOWLEDGE_FETCH_FAILED = 'KB_KNOWLEDGE_FETCH_FAILED'; 7 | export const NO_MATCHES_FOUND = 'No matches found'; 8 | export const NO_MATCHES_FOUND_MESSAGE = 9 | 'Try modifying your search text or filter to find what you\'re looking for'; 10 | export const KB_KNOWLEDGE_TABLE = 'kb_knowledge'; 11 | export const NUMBER_OF_RECORDS_FETCH = '10'; 12 | -------------------------------------------------------------------------------- /src/agent-assist-example/index.js: -------------------------------------------------------------------------------- 1 | import './agent-assist'; 2 | -------------------------------------------------------------------------------- /src/checklist-example/constants.js: -------------------------------------------------------------------------------- 1 | export const CHECKLIST_LOAD_REQUESTED = 'CHECKLIST_LOAD_REQUESTED'; 2 | export const CHECKLIST_LOAD_SUCCEEDED = 'CHECKLIST_LOAD_SUCCEEDED'; 3 | export const CHECKLIST_INPUT_CHANGED = 'CHECKLIST_INPUT_CHANGED'; 4 | export const CHECKLIST_ITEM_ADD = 'CHECKLIST_ITEM_ADD'; 5 | export const CHECKLIST_ITEM_UPDATED = 'CHECKLIST_ITEM_UPDATED'; 6 | export const CHECKLIST_UPDATED = 'CHECKLIST_UPDATED'; 7 | export const TOGGLE_CLICKED = 'NOW_TOGGLE#CHECKED_SET'; 8 | export const REMOVE_BTN_CLICKED = 'NOW_BUTTON_ICONIC#CLICKED'; 9 | export const URL_CURRENT_USER = 'api/now/ui/user/current_user'; 10 | export const URL_TASK_TABLE = '/api/now/table/vtb_task'; 11 | export const URL_UPDATE_TASK = '/api/now/table/vtb_task/:id'; 12 | export const FETCH_ITEM_REQUESTED = 'FETCH_ITEM_REQUESTED'; 13 | export const FETCH_ITEM_SUCCEEDED = 'FETCH_ITEM_SUCCEEDED'; 14 | export const FETCH_ITEM_FAILED = 'FETCH_ITEM_FAILED'; 15 | export const CREATE_ITEM_REQUESTED = 'CREATE_ITEM_REQUESTED'; 16 | export const CREATE_ITEM_SUCCEEDED = 'CREATE_ITEM_SUCCEEDED'; 17 | export const UPDATE_ITEM_REQUESTED = 'UPDATE_ITEM_REQUESTED'; 18 | export const DELETE_ITEM_REQUESTED = 'DELETE_ITEM_REQUESTED'; 19 | export const CHECKLIST_LOAD_STARTED = 'CHECKLIST_LOAD_STARTED'; 20 | export const ENTER_KEY_CODE = 13; 21 | export const ESC_KEY_CODE = 27; 22 | export const FILTER = { 23 | ALL: 'all', 24 | COMPLETE: 'complete', 25 | INCOMPLETE: 'incomplete' 26 | }; 27 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist-item/actions.js: -------------------------------------------------------------------------------- 1 | import {TOGGLE_CLICKED, CHECKLIST_ITEM_UPDATED} from '../constants'; 2 | 3 | export default { 4 | actionHandlers: { 5 | [TOGGLE_CLICKED]: { 6 | stopPropagation: true, 7 | effect: ({state, action, dispatch}) => { 8 | action.preventDefault(); 9 | const {itemId} = state.properties; 10 | const {value} = action.payload; 11 | dispatch(CHECKLIST_ITEM_UPDATED, {itemId, active: value}); 12 | } 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist-item/checklist-item.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import styles from './checklist-item.scss'; 4 | import view from './view'; 5 | import checklistItemAction from './actions'; 6 | 7 | createCustomElement('example-checklist-item', { 8 | renderer: {type: snabbdom}, 9 | view, 10 | properties: { 11 | itemId: { 12 | default: '' 13 | }, 14 | label: { 15 | default: '' 16 | }, 17 | active: { 18 | default: false 19 | }, 20 | editing: { 21 | default: false 22 | } 23 | }, 24 | styles, 25 | ...checklistItemAction 26 | }); 27 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist-item/checklist-item.scss: -------------------------------------------------------------------------------- 1 | @import '@servicenow/sass-kit/host'; 2 | 3 | :host { 4 | display: block; 5 | } 6 | 7 | .now-checklist-item { 8 | display: grid; 9 | align-items: center; 10 | grid-template-columns: now-fn-px2rem(120px) 1fr now-fn-px2rem(32px); 11 | border-bottom-width: 1px; 12 | border-bottom-style: solid; 13 | border-bottom-color: RGB($now-color--divider-secondary); 14 | } 15 | 16 | .now-checklist-item-cell { 17 | display: flex; 18 | padding: $now-global-space--sm; 19 | 20 | &:first-child { 21 | border-right-width: 1px; 22 | border-right-style: solid; 23 | border-right-color: RGB($now-color--divider-secondary); 24 | } 25 | 26 | &.-center { 27 | justify-content: center; 28 | } 29 | } 30 | 31 | .now-checklist-item-input { 32 | margin: 0; 33 | padding: 0; 34 | font-size: inherit; 35 | line-height: inherit; 36 | font-family: inherit; 37 | } 38 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist-item/index.js: -------------------------------------------------------------------------------- 1 | import './checklist-item'; 2 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist-item/view.js: -------------------------------------------------------------------------------- 1 | import '@servicenow/now-toggle'; 2 | import { 3 | CHECKLIST_ITEM_UPDATED, 4 | ENTER_KEY_CODE, 5 | ESC_KEY_CODE 6 | } from '../constants'; 7 | 8 | export default (state, {dispatch, updateProperties}) => { 9 | const { 10 | properties: {itemId, label, active, editing} 11 | } = state; 12 | const setEditing = editing => updateProperties({editing}); 13 | 14 | const labelCell = ( 15 | setEditing(true)}> 19 | {label} 20 | 21 | ); 22 | 23 | const inputCell = ( 24 | 25 | vnode.elm.focus()} 29 | on-keydown={({keyCode, target: {value: label}}) => { 30 | const newLabel = label.trim(); 31 | if (keyCode === ENTER_KEY_CODE) { 32 | setEditing(false); 33 | if (newLabel) { 34 | dispatch(CHECKLIST_ITEM_UPDATED, { 35 | itemId, 36 | short_description: newLabel 37 | }); 38 | } 39 | } else if (keyCode === ESC_KEY_CODE) { 40 | setEditing(false); 41 | } 42 | }} 43 | on-blur={() => setEditing(false)} 44 | /> 45 | 46 | ); 47 | 48 | return ( 49 |
50 | 51 | 52 | 53 | {editing ? inputCell : labelCell} 54 | 61 |
62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist/actions.js: -------------------------------------------------------------------------------- 1 | import {actionTypes} from '@servicenow/ui-core'; 2 | import {createHttpEffect} from '@servicenow/ui-effect-http'; 3 | import { 4 | CHECKLIST_LOAD_SUCCEEDED, 5 | CHECKLIST_ITEM_ADD, 6 | CHECKLIST_ITEM_UPDATED, 7 | URL_CURRENT_USER, 8 | FETCH_ITEM_REQUESTED, 9 | URL_TASK_TABLE, 10 | FETCH_ITEM_SUCCEEDED, 11 | FETCH_ITEM_FAILED, 12 | CREATE_ITEM_REQUESTED, 13 | CREATE_ITEM_SUCCEEDED, 14 | URL_UPDATE_TASK, 15 | UPDATE_ITEM_REQUESTED, 16 | REMOVE_BTN_CLICKED, 17 | DELETE_ITEM_REQUESTED, 18 | CHECKLIST_LOAD_STARTED, 19 | CHECKLIST_LOAD_REQUESTED 20 | } from '../constants'; 21 | 22 | export default { 23 | actionHandlers: { 24 | [actionTypes.COMPONENT_BOOTSTRAPPED]: ({dispatch}) => { 25 | dispatch(CHECKLIST_LOAD_REQUESTED); 26 | }, 27 | [CHECKLIST_LOAD_REQUESTED]: createHttpEffect(URL_CURRENT_USER, { 28 | startActionType: CHECKLIST_LOAD_STARTED, 29 | successActionType: CHECKLIST_LOAD_SUCCEEDED 30 | }), 31 | [CHECKLIST_LOAD_STARTED]: ({updateState}) => { 32 | updateState({isLoading: true}); 33 | }, 34 | [CHECKLIST_LOAD_SUCCEEDED]: ({ 35 | action: { 36 | payload: {result = {}} 37 | }, 38 | dispatch, 39 | updateState 40 | }) => { 41 | const {user_sys_id: userSysId} = result; 42 | if (userSysId) { 43 | updateState({userSysId}); 44 | dispatch(FETCH_ITEM_REQUESTED, { 45 | sysparm_fields: 'sys_id,short_description,active,assigned_to', 46 | sysparm_query: `assigned_to=${userSysId}^ORDERBYDESCsys_created_on` 47 | }); 48 | } else { 49 | updateState({isLoading: false}); 50 | } 51 | }, 52 | [FETCH_ITEM_REQUESTED]: createHttpEffect(URL_TASK_TABLE, { 53 | queryParams: ['sysparm_fields', 'sysparm_query'], 54 | errorActionType: FETCH_ITEM_FAILED, 55 | successActionType: FETCH_ITEM_SUCCEEDED 56 | }), 57 | [FETCH_ITEM_SUCCEEDED]: ({action, updateState}) => { 58 | const { 59 | payload: {result = []} 60 | } = action; 61 | updateState({isLoading: false}); 62 | updateState({ 63 | path: 'items', 64 | operation: 'concat', 65 | value: result.map(item => ({ 66 | short_description: item.short_description, 67 | active: item.active === 'true', 68 | itemId: item.sys_id 69 | })) 70 | }); 71 | }, 72 | [CHECKLIST_ITEM_ADD]: ({action, updateState, dispatch, state}) => { 73 | const { 74 | payload: {inputValue} 75 | } = action; 76 | const {userSysId} = state; 77 | updateState({inputValue: ''}); 78 | dispatch(CREATE_ITEM_REQUESTED, { 79 | data: { 80 | short_description: inputValue, 81 | assigned_to: userSysId, 82 | active: false 83 | } 84 | }); 85 | }, 86 | [CREATE_ITEM_REQUESTED]: createHttpEffect(URL_TASK_TABLE, { 87 | method: 'POST', 88 | dataParam: 'data', 89 | successActionType: CREATE_ITEM_SUCCEEDED 90 | }), 91 | [CREATE_ITEM_SUCCEEDED]: ({action, updateState}) => { 92 | const { 93 | payload: {result} 94 | } = action; 95 | updateState({ 96 | path: 'items', 97 | operation: 'unshift', 98 | value: { 99 | short_description: result.short_description, 100 | active: result.active === 'true', 101 | itemId: result.sys_id 102 | } 103 | }); 104 | }, 105 | [CHECKLIST_ITEM_UPDATED]: ({action, updateState, state, dispatch}) => { 106 | const {payload} = action; 107 | updateState({ 108 | items: state.items.map(item => 109 | item.itemId === payload.itemId 110 | ? { 111 | ...item, 112 | ...action.payload 113 | } 114 | : {...item} 115 | ) 116 | }); 117 | dispatch(UPDATE_ITEM_REQUESTED, { 118 | data: {...payload}, 119 | id: payload.itemId 120 | }); 121 | }, 122 | [UPDATE_ITEM_REQUESTED]: createHttpEffect(URL_UPDATE_TASK, { 123 | method: 'PUT', 124 | dataParam: 'data', 125 | pathParams: ['id'] 126 | }), 127 | [REMOVE_BTN_CLICKED]: ({action, updateState, dispatch, state}) => { 128 | const { 129 | payload: {itemId} 130 | } = action; 131 | updateState({ 132 | items: state.items.filter(item => item.itemId !== itemId) 133 | }); 134 | dispatch(DELETE_ITEM_REQUESTED, {id: itemId}); 135 | }, 136 | [DELETE_ITEM_REQUESTED]: createHttpEffect(URL_UPDATE_TASK, { 137 | method: 'DELETE', 138 | pathParams: ['id'] 139 | }) 140 | } 141 | }; 142 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist/checklist.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import view from './view'; 4 | import styles from './checklist.scss'; 5 | import checklistActions from './actions'; 6 | import rtlBehavior from '@servicenow/behavior-rtl'; 7 | import {FILTER} from '../constants'; 8 | 9 | createCustomElement('example-checklist', { 10 | renderer: {type: snabbdom}, 11 | view, 12 | initialState: { 13 | inputValue: '', 14 | userSysId: '', 15 | items: [], 16 | isLoading: false 17 | }, 18 | properties: { 19 | itemsLeft: { 20 | computed({items = []}) { 21 | return items.filter(item => !item.active).length; 22 | } 23 | }, 24 | filter: { 25 | default: FILTER.ALL 26 | } 27 | }, 28 | ...checklistActions, 29 | styles, 30 | behaviors: [rtlBehavior] 31 | }); 32 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist/checklist.scss: -------------------------------------------------------------------------------- 1 | @import '@servicenow/sass-kit/host'; 2 | 3 | :host { 4 | display: block; 5 | } 6 | 7 | .now-checklist { 8 | max-width: now-fn-px2rem(800px); 9 | margin: $now-global-space--md auto; 10 | padding: $now-global-space--xxl; 11 | box-shadow: $now-global-drop-shadow--md; 12 | background-color: RGB($now-color--background-primary); 13 | now-loader { 14 | margin-top: $now-global-space--md; 15 | } 16 | } 17 | 18 | .now-checklist-input { 19 | display: block; 20 | width: 100%; 21 | margin-bottom: $now-global-space--lg; 22 | padding: $now-global-space--md; 23 | font-size: $now-global-font-size--lg; 24 | font-family: inherit; 25 | } 26 | 27 | .now-checklist-thead { 28 | display: grid; 29 | grid-template-columns: now-fn-px2rem(120px) 1fr; 30 | border-bottom-width: 1px; 31 | border-bottom-style: solid; 32 | border-bottom-color: RGB($now-color--divider-secondary); 33 | } 34 | 35 | .now-checklist-th { 36 | padding-right: $now-global-space--sm; 37 | padding-bottom: $now-global-space--sm; 38 | padding-left: $now-global-space--sm; 39 | font-weight: bold; 40 | 41 | &.-center { 42 | text-align: center; 43 | } 44 | } 45 | 46 | .now-checklist-footer { 47 | display: flex; 48 | align-items: center; 49 | } 50 | 51 | .now-checklist-button { 52 | @include now-mx-rtl-property(margin-left, auto); 53 | } 54 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist/index.js: -------------------------------------------------------------------------------- 1 | import './checklist'; 2 | -------------------------------------------------------------------------------- /src/checklist-example/example-checklist/view.js: -------------------------------------------------------------------------------- 1 | import '@servicenow/now-heading'; 2 | import '@servicenow/now-button'; 3 | import '@servicenow/now-loader'; 4 | import '@servicenow/now-label-value'; 5 | import '../example-checklist-item'; 6 | import {CHECKLIST_ITEM_ADD, ENTER_KEY_CODE, FILTER} from '../constants'; 7 | 8 | const filterItems = (items, filter) => { 9 | switch (filter) { 10 | case FILTER.ALL: 11 | return items; 12 | case FILTER.INCOMPLETE: 13 | return items.filter(item => !item.active); 14 | case FILTER.COMPLETE: 15 | return items.filter(item => item.active); 16 | } 17 | }; 18 | 19 | export default (state, {dispatch, updateProperties}) => { 20 | const { 21 | properties: {itemsLeft, filter}, 22 | items, 23 | inputValue, 24 | isLoading 25 | } = state; 26 | const filteredItems = filterItems(items, filter); 27 | return ( 28 |
29 |
30 | 31 |
32 | { 38 | const inputValue = value.trim(); 39 | if (keyCode === ENTER_KEY_CODE && inputValue) { 40 | dispatch(CHECKLIST_ITEM_ADD, {inputValue}); 41 | } 42 | }} 43 | /> 44 |
45 |
46 |
47 | 48 | Mark as done 49 | 50 | 51 | Task 52 | 53 |
54 |
55 |
56 | {isLoading ? ( 57 | 58 | ) : ( 59 | filteredItems.map(item => ( 60 | 66 | )) 67 | )} 68 | {!filteredItems.length ? ( 69 | 70 | ) : null} 71 |
72 |
73 |
74 |

75 | {itemsLeft} item{itemsLeft === 1 ? '' : 's'} left 76 | 77 | ({filteredItems.length} visible) 78 | 79 |

80 | updateProperties({filter: FILTER.ALL})} 86 | /> 87 | updateProperties({filter: FILTER.INCOMPLETE})} 93 | /> 94 | updateProperties({filter: FILTER.COMPLETE})} 99 | /> 100 |
101 |
102 | ); 103 | }; 104 | -------------------------------------------------------------------------------- /src/checklist-example/index.js: -------------------------------------------------------------------------------- 1 | import './example-checklist'; 2 | -------------------------------------------------------------------------------- /src/counter-example/example-counter/counter.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import style from './counter.scss'; 4 | import view from './view'; 5 | 6 | createCustomElement('example-counter', { 7 | renderer: {type: snabbdom}, 8 | view, 9 | initialState: { 10 | tally: 0 11 | }, 12 | styles: style 13 | }); 14 | -------------------------------------------------------------------------------- /src/counter-example/example-counter/counter.scss: -------------------------------------------------------------------------------- 1 | .counter { 2 | margin: 15px; 3 | } 4 | 5 | h2 { 6 | font-family: Arial; 7 | color: seagreen; 8 | } 9 | 10 | button { 11 | margin: 4px; 12 | padding: 8px; 13 | border: 1px solid darkgray; 14 | } 15 | 16 | h3 { 17 | align-content: center; 18 | font-size: 16px; 19 | margin: 12px; 20 | } 21 | -------------------------------------------------------------------------------- /src/counter-example/example-counter/index.js: -------------------------------------------------------------------------------- 1 | import './counter.js'; 2 | -------------------------------------------------------------------------------- /src/counter-example/example-counter/view.js: -------------------------------------------------------------------------------- 1 | export default (state, {updateState}) => { 2 | const {tally} = state; 3 | return ( 4 |
5 |

Click Counter

6 | 7 | 12 | 13 | 14 | 17 | 18 |
Value: {tally}
19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/counter-example/index.js: -------------------------------------------------------------------------------- 1 | import './example-counter'; 2 | -------------------------------------------------------------------------------- /src/customer-360-example/actionHandlers.js: -------------------------------------------------------------------------------- 1 | import {actionTypes} from '@servicenow/ui-core'; 2 | import {getHttpEffect} from './getHttpEffect'; 3 | import { 4 | INCIDENT_FETCH_REQUESTED, 5 | INCIDENT_FETCH_SUCCEEDED, 6 | INCIDENT_FETCH_FAILED, 7 | USER_FETCH_REQUESTED, 8 | USER_FETCH_SUCCEEDED, 9 | USER_FETCH_FAILED, 10 | INCIDENT_TABLE, 11 | FETCH_COMPANY, 12 | FETCH_LOCATION, 13 | COMPANY_FETCH_SUCCEEDED, 14 | LOCATION_FETCH_SUCCEEDED, 15 | LOCATION_TABLE, 16 | COMPANY_TABLE, 17 | USER_TABLE, 18 | OPEN_USER_DETAILS, 19 | PREVIEW_RECORD 20 | } from './constants'; 21 | 22 | export default { 23 | [actionTypes.COMPONENT_BOOTSTRAPPED]: ({ 24 | dispatch, 25 | updateState, 26 | properties 27 | }) => { 28 | const {table, sysid} = properties; 29 | 30 | if (table === INCIDENT_TABLE && sysid) { 31 | updateState({isLoading: true}); 32 | dispatch(INCIDENT_FETCH_REQUESTED, {sysId: sysid, table}); 33 | } 34 | }, 35 | [INCIDENT_FETCH_REQUESTED]: getHttpEffect({ 36 | successActionType: INCIDENT_FETCH_SUCCEEDED, 37 | errorActionType: INCIDENT_FETCH_FAILED 38 | }), 39 | [INCIDENT_FETCH_SUCCEEDED]: ({dispatch, action, updateState}) => { 40 | const { 41 | payload: {result} 42 | } = action; 43 | 44 | if (result && result.caller_id && result.caller_id.value) { 45 | dispatch(USER_FETCH_REQUESTED, { 46 | sysId: result.caller_id.value, 47 | table: USER_TABLE 48 | }); 49 | } else { 50 | updateState({isLoading: false}); 51 | } 52 | }, 53 | [USER_FETCH_REQUESTED]: getHttpEffect({ 54 | successActionType: USER_FETCH_SUCCEEDED, 55 | errorActionType: USER_FETCH_FAILED 56 | }), 57 | [USER_FETCH_SUCCEEDED]: ({action, dispatch, updateState}) => { 58 | const { 59 | payload: {result} 60 | } = action; 61 | 62 | if (!result) { 63 | updateState({isLoading: false}); 64 | } else { 65 | const location = result.location && result.location.value; 66 | if (location) 67 | dispatch(FETCH_LOCATION, {sysId: location, table: LOCATION_TABLE}); 68 | 69 | const company = result.company && result.company.value; 70 | if (company) 71 | dispatch(FETCH_COMPANY, {sysId: company, table: COMPANY_TABLE}); 72 | 73 | updateState({ 74 | userResult: result, 75 | isLoading: false 76 | }); 77 | } 78 | }, 79 | [FETCH_LOCATION]: getHttpEffect({ 80 | successActionType: LOCATION_FETCH_SUCCEEDED 81 | }), 82 | [FETCH_COMPANY]: getHttpEffect({ 83 | successActionType: COMPANY_FETCH_SUCCEEDED 84 | }), 85 | [COMPANY_FETCH_SUCCEEDED]: ({updateState, action}) => { 86 | const { 87 | payload: {result} 88 | } = action; 89 | 90 | if (result) updateState({companyResult: result}); 91 | }, 92 | [LOCATION_FETCH_SUCCEEDED]: ({updateState, action}) => { 93 | const { 94 | payload: {result} 95 | } = action; 96 | 97 | if (result) updateState({locationResult: result}); 98 | }, 99 | [INCIDENT_FETCH_FAILED]: ({updateState}) => updateState({isLoading: false}), 100 | [USER_FETCH_FAILED]: ({updateState}) => updateState({isLoading: false}), 101 | [OPEN_USER_DETAILS]: ({state, dispatch}) => { 102 | const { 103 | userResult: {sys_id} 104 | } = state; 105 | 106 | dispatch(PREVIEW_RECORD, { 107 | table: USER_TABLE, 108 | sys_id 109 | }); 110 | } 111 | }; 112 | -------------------------------------------------------------------------------- /src/customer-360-example/constants.js: -------------------------------------------------------------------------------- 1 | export const INCIDENT_FETCH_REQUESTED = 'INCIDENT_FETCH_REQUESTED'; 2 | export const INCIDENT_FETCH_SUCCEEDED = 'INCIDENT_FETCH_SUCCEEDED'; 3 | export const INCIDENT_FETCH_FAILED = 'INCIDENT_FETCH_FAILED'; 4 | export const USER_FETCH_REQUESTED = 'USER_FETCH_REQUESTED'; 5 | export const USER_FETCH_SUCCEEDED = 'USER_FETCH_SUCCEEDED'; 6 | export const USER_FETCH_FAILED = 'USER_FETCH_FAILED'; 7 | export const LOCATION_FETCH_SUCCEEDED = 'LOCATION_FETCH_SUCCEEDED'; 8 | export const COMPANY_FETCH_SUCCEEDED = 'COMPANY_FETCH_SUCCEEDED'; 9 | export const FETCH_LOCATION = 'FETCH_LOCATION'; 10 | export const FETCH_COMPANY = 'FETCH_COMPANY'; 11 | export const INCIDENT_TABLE = 'incident'; 12 | export const COMPANY_TABLE = 'core_company'; 13 | export const LOCATION_TABLE = 'cmn_location'; 14 | export const USER_TABLE = 'sys_user'; 15 | export const NO_DATA = 'No Data'; 16 | export const PREVIEW_RECORD = 'PREVIEW_RECORD'; 17 | export const OPEN_USER_DETAILS = 'OPEN_USER_DETAILS'; 18 | -------------------------------------------------------------------------------- /src/customer-360-example/customer360.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import '@servicenow/now-avatar'; 4 | import '@servicenow/now-label-value'; 5 | import styles from './styles.scss'; 6 | import view from './view'; 7 | import actionHandlers from './actionHandlers'; 8 | 9 | createCustomElement('example-customer-360', { 10 | renderer: {type: snabbdom}, 11 | view, 12 | initialState: { 13 | userResult: {}, 14 | isLoading: true, 15 | locationResult: {}, 16 | companyResult: {} 17 | }, 18 | properties: { 19 | table: { 20 | default: '' 21 | }, 22 | sysid: { 23 | default: '' 24 | } 25 | }, 26 | styles, 27 | actionHandlers 28 | }); 29 | -------------------------------------------------------------------------------- /src/customer-360-example/getCallerData.js: -------------------------------------------------------------------------------- 1 | import {NO_DATA} from './constants'; 2 | 3 | export default function getCallerData( 4 | userResult, 5 | locationResult, 6 | companyResult 7 | ) { 8 | const {name, photo} = userResult; 9 | const itemOne = [ 10 | {label: 'Name', value: {type: 'string', value: name}}, 11 | { 12 | label: 'Company', 13 | value: { 14 | type: 'string', 15 | value: (companyResult && companyResult.name) || NO_DATA 16 | } 17 | }, 18 | { 19 | label: 'Location', 20 | value: { 21 | type: 'string', 22 | value: (locationResult && locationResult.name) || NO_DATA 23 | } 24 | } 25 | ]; 26 | 27 | const itemTwo = [ 28 | { 29 | label: 'Email', 30 | value: { 31 | type: 'string', 32 | value: userResult.email || NO_DATA 33 | } 34 | }, 35 | { 36 | label: 'Business Phone', 37 | value: { 38 | type: 'string', 39 | value: userResult.phone || NO_DATA 40 | } 41 | }, 42 | { 43 | label: 'City', 44 | value: {type: 'string', value: userResult.city || NO_DATA} 45 | } 46 | ]; 47 | 48 | return { 49 | itemOne, 50 | itemTwo, 51 | name, 52 | photo 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /src/customer-360-example/getHttpEffect.js: -------------------------------------------------------------------------------- 1 | import {createHttpEffect} from '@servicenow/ui-effect-http'; 2 | 3 | export const getHttpEffect = ({successActionType, errorActionType}) => { 4 | return createHttpEffect('/api/now/table/:table/:sysId', { 5 | pathParams: ['sysId', 'table'], 6 | successActionType, 7 | errorActionType 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /src/customer-360-example/index.js: -------------------------------------------------------------------------------- 1 | import './customer360'; 2 | -------------------------------------------------------------------------------- /src/customer-360-example/styles.scss: -------------------------------------------------------------------------------- 1 | @import '@servicenow/sass-kit/host'; 2 | 3 | :host { 4 | height: 100%; 5 | } 6 | .customer-360 { 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: center; 10 | width: now-fn-px2rem(480px); 11 | height: inherit; 12 | border: 1px solid RGB($now-color--divider-secondary); 13 | padding: $now-global-space--md; 14 | background-color: RGB($now-color--background-primary); 15 | header { 16 | width: 100%; 17 | padding-left: $now-global-space--sm; 18 | border-bottom: 1px solid RGB($now-color--divider-secondary); 19 | } 20 | .no-data { 21 | display: grid; 22 | justify-content: center; 23 | justify-items: center; 24 | grid-gap: $now-global-space--md; 25 | } 26 | .customer-details { 27 | display: grid; 28 | grid-template-columns: now-fn-px2rem(80px) now-fn-px2rem(360px); 29 | grid-gap: $now-global-space--md; 30 | padding: $now-global-space--md 0px; 31 | .caller-avatar { 32 | text-align: center; 33 | cursor: pointer; 34 | grid-row: 1/3; 35 | } 36 | now-heading { 37 | cursor: pointer; 38 | align-self: center; 39 | margin-top: $now-global-space--sm; 40 | } 41 | now-label-value-stacked { 42 | grid-column: 2/3; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/customer-360-example/view.js: -------------------------------------------------------------------------------- 1 | import {Fragment} from '@servicenow/ui-renderer-snabbdom'; 2 | import {OPEN_USER_DETAILS} from './constants'; 3 | import getCallerData from './getCallerData'; 4 | import '@servicenow/now-heading'; 5 | import '@servicenow/now-label-value'; 6 | import '@servicenow/now-loader'; 7 | import '@servicenow/now-icon'; 8 | 9 | const customerDetails = ( 10 | userResult, 11 | locationResult, 12 | companyResult, 13 | dispatch 14 | ) => { 15 | const dataFormat = getCallerData(userResult, locationResult, companyResult); 16 | 17 | if (!Object.keys(userResult).length) 18 | return ( 19 |
20 | 21 | 22 |
23 | ); 24 | 25 | return ( 26 | 27 |
28 | 29 |
30 |
31 | { 34 | dispatch(OPEN_USER_DETAILS); 35 | }}> 36 | 43 | 44 | { 48 | dispatch(OPEN_USER_DETAILS); 49 | }} 50 | /> 51 | 58 | 65 |
66 |
67 | ); 68 | }; 69 | 70 | export default (state, {dispatch}) => { 71 | const {userResult, isLoading, locationResult, companyResult} = state; 72 | 73 | return ( 74 |
75 | {isLoading ? ( 76 | 77 | ) : ( 78 | customerDetails(userResult, locationResult, companyResult, dispatch) 79 | )} 80 |
81 | ); 82 | }; 83 | -------------------------------------------------------------------------------- /src/flash-card-example/constants.js: -------------------------------------------------------------------------------- 1 | export const CARDS = [ 2 | { 3 | title: '', 4 | description: 5 | 'A placeholder inside a web component that users can fill with their own markup', 6 | attributes: [ 7 | { 8 | title: 'name', 9 | description: 'The name of the slot.' 10 | } 11 | ] 12 | }, 13 | { 14 | title: '', 15 | description: 16 | 'Use the HTML element with either the canvas scripting API or the WebGL API to draw graphics and animations.', 17 | attributes: [ 18 | { 19 | title: 'height', 20 | description: 21 | 'The height of the coordinate space in CSS pixels. Defaults to 150.' 22 | }, 23 | { 24 | title: 'width', 25 | description: 26 | 'The width of the coordinate space in CSS pixels. Defaults to 300.' 27 | }, 28 | { 29 | title: 'moz-opaque', 30 | description: 31 | 'Lets the canvas know whether or not translucency will be a factor. If the canvas knows there\'s no translucency, painting performance can be optimized. This is only supported by Mozilla-based browsers; use the standardized canvas.getContext(\'2d\', { alpha: false }) instead.' 32 | } 33 | ] 34 | }, 35 | { 36 | title: '', 37 | description: 38 | 'The HTML Title element (<title>) defines the document\'s title that is shown in a browser\'s title bar or a page\'s tab. It only contains text and tags within the element are ignored.' 39 | } 40 | ]; 41 | -------------------------------------------------------------------------------- /src/flash-card-example/example-card-list/card-list.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import '../example-flash-card'; 4 | import {CARDS} from '../constants'; 5 | import view from './view'; 6 | import styles from './card-list.scss'; 7 | 8 | createCustomElement('example-card-list', { 9 | renderer: {type: snabbdom}, 10 | view, 11 | initialState: { 12 | cards: CARDS 13 | }, 14 | styles 15 | }); 16 | -------------------------------------------------------------------------------- /src/flash-card-example/example-card-list/card-list.scss: -------------------------------------------------------------------------------- 1 | .card-list { 2 | display: flex; 3 | } 4 | dt { 5 | margin-bottom: 5px; 6 | span { 7 | background: lightgrey; 8 | padding: 2px 5px; 9 | border-radius: 2px; 10 | } 11 | } 12 | dd { 13 | margin-bottom: 10px; 14 | } 15 | -------------------------------------------------------------------------------- /src/flash-card-example/example-card-list/index.js: -------------------------------------------------------------------------------- 1 | import './card-list.js'; 2 | -------------------------------------------------------------------------------- /src/flash-card-example/example-card-list/view.js: -------------------------------------------------------------------------------- 1 | import {Fragment} from '@servicenow/ui-renderer-snabbdom'; 2 | 3 | const renderAttributes = attributes => ( 4 | <dl slot="attr"> 5 | {attributes.map(attr => { 6 | return ( 7 | <Fragment> 8 | <dt> 9 | <span>{attr.title}</span> 10 | </dt> 11 | <dd>{attr.description}</dd> 12 | </Fragment> 13 | ); 14 | })} 15 | </dl> 16 | ); 17 | 18 | export default ({cards}) => ( 19 | <div className="card-list"> 20 | {cards.map(card => ( 21 | <example-flash-card> 22 | <span slot="title">{card.title}</span> 23 | <span slot="desc">{card.description}</span> 24 | {card.attributes && 25 | card.attributes.length && 26 | renderAttributes(card.attributes)} 27 | </example-flash-card> 28 | ))} 29 | </div> 30 | ); 31 | -------------------------------------------------------------------------------- /src/flash-card-example/example-flash-card/flash-card.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import styles from './flash-card.scss'; 4 | import view from './view'; 5 | 6 | createCustomElement('example-flash-card', { 7 | renderer: {type: snabbdom}, 8 | view, 9 | styles 10 | }); 11 | -------------------------------------------------------------------------------- /src/flash-card-example/example-flash-card/flash-card.scss: -------------------------------------------------------------------------------- 1 | $color-blue: #217ac0; 2 | $color-black: black; 3 | $color-white: white; 4 | 5 | .card { 6 | width: 300px; 7 | padding: 10px; 8 | margin: 10px; 9 | border: 1px; 10 | border-color: $color-black; 11 | border-style: solid; 12 | border-radius: 5px; 13 | 14 | .card-title { 15 | font-weight: bold; 16 | color: $color-blue; 17 | margin-right: 10px; 18 | font-size: 20px; 19 | } 20 | .card-attributes { 21 | h4 > span { 22 | padding: 2px 5px; 23 | color: $color-white; 24 | background: $color-blue; 25 | border-radius: 5px; 26 | } 27 | } 28 | } 29 | 30 | ::slotted(dl) { 31 | padding-left: 10px; 32 | } 33 | -------------------------------------------------------------------------------- /src/flash-card-example/example-flash-card/index.js: -------------------------------------------------------------------------------- 1 | import './flash-card.js'; 2 | -------------------------------------------------------------------------------- /src/flash-card-example/example-flash-card/view.js: -------------------------------------------------------------------------------- 1 | export default () => ( 2 | <div className="card"> 3 | <summary> 4 | <span className="card-title"> 5 | <slot name="title" /> 6 | </span> 7 | <span> 8 | <slot name="desc" /> 9 | </span> 10 | </summary> 11 | <div className="card-attributes"> 12 | <h4> 13 | <span>Attributes</span> 14 | </h4> 15 | <slot name="attr"> 16 | <p>None</p> 17 | </slot> 18 | </div> 19 | </div> 20 | ); 21 | -------------------------------------------------------------------------------- /src/flash-card-example/index.js: -------------------------------------------------------------------------------- 1 | import './example-card-list'; 2 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './agent-assist-example'; 2 | import './checklist-example'; 3 | import './counter-example'; 4 | import './customer-360-example'; 5 | import './flash-card-example'; 6 | import './task-board-example'; 7 | -------------------------------------------------------------------------------- /src/task-board-example/behaviors/dragDropBehaviors.js: -------------------------------------------------------------------------------- 1 | import {actionTypes} from '@servicenow/ui-core'; 2 | 3 | const {COMPONENT_BOOTSTRAPPED} = actionTypes; 4 | 5 | export const DROPPED = 'DROPPED'; 6 | 7 | export const dropBehavior = { 8 | name: 'dropBehavior', 9 | eventHandlers: [ 10 | { 11 | events: ['dragover'], 12 | effect({action: {payload: {event}}}) { 13 | event.preventDefault(); 14 | } 15 | }, 16 | { 17 | events: ['drop'], 18 | effect({ 19 | dispatch, 20 | action: { 21 | payload: {event} 22 | } 23 | }) { 24 | const data = JSON.parse(event.dataTransfer.getData('application/json')); 25 | dispatch(DROPPED, {data}); 26 | } 27 | } 28 | ] 29 | }; 30 | 31 | export const dragBehavior = { 32 | name: 'dragBehavior', 33 | properties: {draggable: {default: 'true', reflect: true}}, 34 | actionHandlers: { 35 | [COMPONENT_BOOTSTRAPPED]({updateState, action: {payload: {options}}}) { 36 | updateState({getData: options.getData}) 37 | } 38 | }, 39 | eventHandlers: [ 40 | { 41 | events: ['dragstart'], 42 | effect(coeffects) { 43 | const { 44 | state: {getData}, 45 | action: {payload: {event}} 46 | } = coeffects; 47 | 48 | const data = getData(coeffects); 49 | event.dataTransfer.setData('application/json', JSON.stringify(data)); 50 | } 51 | } 52 | ] 53 | }; 54 | -------------------------------------------------------------------------------- /src/task-board-example/constants.js: -------------------------------------------------------------------------------- 1 | export const CARD_DRAGGED = 'CARD_DRAGGED'; 2 | export const CARD_DROPPED = 'CARD_DROPPED'; 3 | export const LANES = [ 4 | { 5 | laneId: 0, 6 | title: 'ToDo' 7 | }, 8 | { 9 | laneId: 1, 10 | title: 'Work in Progress' 11 | }, 12 | { 13 | laneId: 2, 14 | title: 'Done' 15 | } 16 | ]; 17 | export const CARDS = [ 18 | { 19 | cardId: 0, 20 | title: 'Read Now Experience UI Framework 101', 21 | lane: 0 22 | }, 23 | { 24 | cardId: 1, 25 | title: 'Try out Counter Example', 26 | lane: 0 27 | }, 28 | { 29 | cardId: 2, 30 | title: 'Try out Checklist Example', 31 | lane: 0 32 | }, 33 | { 34 | cardId: 3, 35 | title: 'Read Advanced Section', 36 | lane: 0 37 | } 38 | ]; 39 | -------------------------------------------------------------------------------- /src/task-board-example/example-card/card.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import styles from './card.scss'; 4 | import {dragBehavior} from '../behaviors/dragDropBehaviors'; 5 | 6 | const view = ({properties: {title}}) => <div className="card">{title}</div>; 7 | 8 | createCustomElement('example-card', { 9 | renderer: {type: snabbdom}, 10 | view, 11 | properties: { 12 | cardId: { 13 | default: 0, 14 | reflect: false 15 | }, 16 | title: {}, 17 | lane: {} 18 | }, 19 | styles, 20 | behaviors: [{ 21 | behavior: dragBehavior, 22 | options: { 23 | getData({properties: {cardId, lane}}) { 24 | return {cardId, lane}; 25 | } 26 | } 27 | }] 28 | }); 29 | -------------------------------------------------------------------------------- /src/task-board-example/example-card/card.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | } 4 | 5 | .card { 6 | height: 100px; 7 | border: 1px solid #2e2e2e; 8 | margin: 0 auto 10px auto; 9 | text-align: center; 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | background: rgba(0, 0, 0, 0.6); 14 | border-radius: 5px; 15 | } 16 | -------------------------------------------------------------------------------- /src/task-board-example/example-card/index.js: -------------------------------------------------------------------------------- 1 | import './card.js'; 2 | -------------------------------------------------------------------------------- /src/task-board-example/example-lane/index.js: -------------------------------------------------------------------------------- 1 | import './lane.js'; 2 | -------------------------------------------------------------------------------- /src/task-board-example/example-lane/lane.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import '../example-card'; 4 | import styles from './lane.scss'; 5 | import { 6 | dropBehavior, 7 | DROPPED 8 | } from '../behaviors/dragDropBehaviors'; 9 | import {CARD_DROPPED} from '../constants'; 10 | import view from './view'; 11 | 12 | createCustomElement('example-lane', { 13 | renderer: {type: snabbdom}, 14 | view, 15 | properties: { 16 | laneId: { 17 | default: 0 18 | }, 19 | title: { 20 | default: '' 21 | }, 22 | cards: { 23 | default: [] 24 | } 25 | }, 26 | styles, 27 | behaviors: [{behavior: dropBehavior}], 28 | actionHandlers: { 29 | [DROPPED]({ 30 | properties: {laneId}, 31 | dispatch, 32 | action: { 33 | payload: {data} 34 | } 35 | }) { 36 | const nextCard = {...data, lane: laneId}; 37 | dispatch(CARD_DROPPED, nextCard); 38 | } 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /src/task-board-example/example-lane/lane.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: inherit; 4 | } 5 | header { 6 | text-align: center; 7 | height: 50px; 8 | line-height: 50px; 9 | padding: 0px 10px; 10 | } 11 | .lane { 12 | background: rgba(0, 0, 0, 0.2); 13 | } 14 | -------------------------------------------------------------------------------- /src/task-board-example/example-lane/view.js: -------------------------------------------------------------------------------- 1 | export default state => { 2 | const { 3 | properties: {title, cards} 4 | } = state; 5 | return ( 6 | <div className="lane"> 7 | <header>{title}</header> 8 | {cards.map(card => ( 9 | <example-card 10 | key={card.cardId} 11 | card-id={card.cardId} 12 | title={card.title} 13 | lane={card.lane} 14 | /> 15 | ))} 16 | </div> 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/task-board-example/example-task-board/actions.js: -------------------------------------------------------------------------------- 1 | import {CARD_DROPPED} from '../constants'; 2 | 3 | export default { 4 | actionHandlers: { 5 | [CARD_DROPPED]: { 6 | stopPropagation: true, 7 | effect: ({state, updateState, action}) => { 8 | updateState({ 9 | cards: state.cards.map(card => { 10 | if (card.cardId === action.payload.cardId) 11 | return { 12 | ...card, 13 | lane: action.payload.lane 14 | }; 15 | 16 | return card; 17 | }) 18 | }) 19 | } 20 | } 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/task-board-example/example-task-board/index.js: -------------------------------------------------------------------------------- 1 | import './task-board.js'; 2 | -------------------------------------------------------------------------------- /src/task-board-example/example-task-board/task-board.js: -------------------------------------------------------------------------------- 1 | import {createCustomElement} from '@servicenow/ui-core'; 2 | import snabbdom from '@servicenow/ui-renderer-snabbdom'; 3 | import styles from './task-board.scss'; 4 | import view from './view'; 5 | import taskBoardActions from './actions'; 6 | import {LANES, CARDS} from '../constants'; 7 | 8 | createCustomElement('example-task-board', { 9 | renderer: {type: snabbdom}, 10 | view, 11 | initialState: { 12 | lanes: LANES, 13 | cards: CARDS 14 | }, 15 | styles, 16 | ...taskBoardActions 17 | }); 18 | -------------------------------------------------------------------------------- /src/task-board-example/example-task-board/task-board.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | text-align: center; 4 | height: 100%; 5 | } 6 | .task-board { 7 | display: flex; 8 | background: rgba(0, 0, 0, 0.8); 9 | width: 70%; 10 | height: inherit; 11 | margin: 0 auto; 12 | color: #ffffff; 13 | } 14 | 15 | .lane-container { 16 | flex: 1; 17 | margin: 0 10px; 18 | justify-content: center; 19 | height: inherit; 20 | } 21 | -------------------------------------------------------------------------------- /src/task-board-example/example-task-board/view.js: -------------------------------------------------------------------------------- 1 | import '../example-lane'; 2 | 3 | export default state => { 4 | const {lanes, cards} = state; 5 | return ( 6 | <div className="task-board"> 7 | {lanes.map(lane => ( 8 | <div className="lane-container"> 9 | <example-lane 10 | key={lane.laneId} 11 | laneId={lane.laneId} 12 | title={lane.title} 13 | cards={cards.filter(card => card.lane === lane.laneId)} 14 | /> 15 | </div> 16 | ))} 17 | </div> 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /src/task-board-example/index.js: -------------------------------------------------------------------------------- 1 | import './example-task-board'; 2 | --------------------------------------------------------------------------------