Merge branch 'master' into dansby/put-litter-in-its-place

This commit is contained in:
Ben Dansby 2021-11-12 10:12:35 -08:00
commit 97c8a9fc7b
65 changed files with 3554 additions and 812 deletions

View File

@ -5,7 +5,10 @@ Nri.Ui.Menu.V1,upgrade to V3
Nri.Ui.Menu.V2,upgrade to V3
Nri.Ui.Modal.V10,upgrade to V11
Nri.Ui.RadioButton.V1,upgrade to V2
Nri.Ui.Select.V5,upgrade to V7
Nri.Ui.Select.V5,upgrade to V8
Nri.Ui.Select.V7,upgrade to V8
Nri.Ui.Table.V4,upgrade to V5
Nri.Ui.Tabs.V6,upgrade to V7
Nri.Ui.Text.V5,upgrade to V6
Nri.Ui.TextInput.V6,upgrade to V7
Nri.Ui.Tooltip.V1,upgrade to V2

1 Nri.Ui.Accordion.V1 upgrade to V3
5 Nri.Ui.Menu.V2 upgrade to V3
6 Nri.Ui.Modal.V10 upgrade to V11
7 Nri.Ui.RadioButton.V1 upgrade to V2
8 Nri.Ui.Select.V5 upgrade to V7 upgrade to V8
9 Nri.Ui.Select.V7 upgrade to V8
10 Nri.Ui.Table.V4 upgrade to V5
11 Nri.Ui.Tabs.V6 upgrade to V7
12 Nri.Ui.Text.V5 upgrade to V6
13 Nri.Ui.TextInput.V6 upgrade to V7
14 Nri.Ui.Tooltip.V1 upgrade to V2

View File

@ -3,7 +3,7 @@
"name": "NoRedInk/noredink-ui",
"summary": "UI Widgets we use at NRI",
"license": "BSD-3-Clause",
"version": "14.6.0",
"version": "14.9.0",
"exposed-modules": [
"Nri.Ui",
"Nri.Ui.Accordion.V1",
@ -51,6 +51,7 @@
"Nri.Ui.SegmentedControl.V14",
"Nri.Ui.Select.V5",
"Nri.Ui.Select.V7",
"Nri.Ui.Select.V8",
"Nri.Ui.Slide.V1",
"Nri.Ui.SlideModal.V2",
"Nri.Ui.SortableTable.V2",
@ -61,9 +62,11 @@
"Nri.Ui.Tabs.V6",
"Nri.Ui.Tabs.V7",
"Nri.Ui.Text.V5",
"Nri.Ui.Text.V6",
"Nri.Ui.Text.Writing.V1",
"Nri.Ui.TextArea.V4",
"Nri.Ui.TextInput.V6",
"Nri.Ui.TextInput.V7",
"Nri.Ui.Tooltip.V1",
"Nri.Ui.Tooltip.V2",
"Nri.Ui.UiIcon.V1"

View File

@ -33,11 +33,7 @@ hint = 'Use Accessibility.Widgetd.Widget'
[forbidden.Html]
hint = 'Use Html.Styled'
usages = [
'styleguide-app/../src/Nri/Ui/Button/V8.elm',
'styleguide-app/Examples/Modal.elm',
'styleguide-app/Main.elm',
]
usages = ['styleguide-app/../src/Nri/Ui/Button/V8.elm']
[forbidden."Nri.Ui.Accordion.V1"]
hint = 'upgrade to V3'
@ -95,7 +91,10 @@ hint = 'upgrade to V14'
hint = 'upgrade to V14'
[forbidden."Nri.Ui.Select.V5"]
hint = 'upgrade to V7'
hint = 'upgrade to V8'
[forbidden."Nri.Ui.Select.V7"]
hint = 'upgrade to V8'
[forbidden."Nri.Ui.Table.V4"]
hint = 'upgrade to V5'
@ -122,6 +121,12 @@ usages = [
'styleguide-app/Examples/Tooltip.elm',
]
[forbidden."Nri.Ui.Text.V5"]
hint = 'upgrade to V6'
[forbidden."Nri.Ui.TextInput.V6"]
hint = 'upgrade to V7'
[forbidden."Nri.Ui.Tooltip.V1"]
hint = 'upgrade to V2'
usages = ['styleguide-app/../src/Nri/Ui/Menu/V1.elm']

462
package-lock.json generated
View File

@ -394,6 +394,15 @@
"winston": "^3.0.0"
},
"dependencies": {
"agent-base": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"dev": true,
"requires": {
"es6-promisify": "^5.0.0"
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@ -406,6 +415,120 @@
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"extract-zip": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
"integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
"dev": true,
"requires": {
"concat-stream": "^1.6.2",
"debug": "^2.6.9",
"mkdirp": "^0.5.4",
"yauzl": "^2.10.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
"https-proxy-agent": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"dev": true,
"requires": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
}
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"puppeteer": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz",
"integrity": "sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ==",
"dev": true,
"requires": {
"debug": "^4.1.0",
"extract-zip": "^1.6.6",
"https-proxy-agent": "^2.2.1",
"mime": "^2.0.3",
"progress": "^2.0.1",
"proxy-from-env": "^1.0.0",
"rimraf": "^2.6.1",
"ws": "^6.1.0"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"ws": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
"dev": true,
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
@ -435,6 +558,11 @@
"@types/node": "*"
}
},
"@types/mime-types": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz",
"integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw=="
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -444,8 +572,16 @@
"@types/node": {
"version": "12.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.7.tgz",
"integrity": "sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==",
"dev": true
"integrity": "sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w=="
},
"@types/yauzl": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz",
"integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==",
"optional": true,
"requires": {
"@types/node": "*"
}
},
"JSONStream": {
"version": "1.3.4",
@ -511,12 +647,9 @@
}
},
"agent-base": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"requires": {
"es6-promisify": "^5.0.0"
}
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
"integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g=="
},
"ansi-escapes": {
"version": "3.2.0",
@ -648,7 +781,8 @@
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
"dev": true
},
"asynckit": {
"version": "0.4.0",
@ -669,9 +803,9 @@
"dev": true
},
"axe-core": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.3.0.tgz",
"integrity": "sha512-54XaTd2VB7A6iBnXMUG2LnBOI7aRbnrVxC5Tz+rVUwYl9MX/cIJc/Ll32YUoFIE/e9UKWMZoQenQu9dFrQyZCg=="
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.3.tgz",
"integrity": "sha512-HZpLE7xu05+8AbpqXITGdxp1Xwk8ysAXrg7MiKRY27py3DAyEJpoJQo1727pWF3F+O79V3r+cTWhOzfB49P89w=="
},
"axios": {
"version": "0.19.0",
@ -699,8 +833,7 @@
"base64-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
"dev": true
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
@ -711,6 +844,52 @@
"tweetnacl": "^0.14.3"
}
},
"bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
},
"dependencies": {
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"bluebird": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz",
@ -932,16 +1111,21 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
"integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
"dev": true,
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
},
"buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"buffer-xor": {
"version": "1.0.3",
@ -1007,6 +1191,11 @@
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
},
"chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
@ -1208,6 +1397,7 @@
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
@ -1266,7 +1456,8 @@
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
"cors": {
"version": "2.8.5",
@ -1373,6 +1564,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
@ -1558,6 +1750,14 @@
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"dev": true
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"env-variable": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz",
@ -1576,7 +1776,8 @@
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
"dev": true
},
"es6-promise-pool": {
"version": "2.5.0",
@ -1588,6 +1789,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"dev": true,
"requires": {
"es6-promise": "^4.0.3"
}
@ -1691,14 +1893,29 @@
"dev": true
},
"extract-zip": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
"integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"requires": {
"concat-stream": "1.6.2",
"debug": "2.6.9",
"mkdirp": "0.5.1",
"yauzl": "2.4.1"
"@types/yauzl": "^2.9.1",
"debug": "^4.1.1",
"get-stream": "^5.1.0",
"yauzl": "^2.10.0"
},
"dependencies": {
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"extsprintf": {
@ -1801,9 +2018,9 @@
}
},
"fd-slicer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
"integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
"requires": {
"pend": "~1.2.0"
}
@ -1884,6 +2101,11 @@
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"dev": true
},
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -1907,6 +2129,14 @@
"integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==",
"dev": true
},
"get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"requires": {
"pump": "^3.0.0"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@ -2093,20 +2323,20 @@
"dev": true
},
"https-proxy-agent": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
"integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
"requires": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
"agent-base": "5",
"debug": "4"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"requires": {
"ms": "^2.1.1"
"ms": "2.1.2"
}
},
"ms": {
@ -2134,8 +2364,7 @@
"ieee754": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
"integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==",
"dev": true
"integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA=="
},
"ignore": {
"version": "5.1.4",
@ -2274,7 +2503,8 @@
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"isexe": {
"version": "2.0.0",
@ -2500,9 +2730,9 @@
}
},
"mime": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
"integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz",
"integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg=="
},
"mime-db": {
"version": "1.36.0",
@ -2549,6 +2779,7 @@
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
},
@ -2556,10 +2787,16 @@
"minimist": {
"version": "0.0.8",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}
}
},
"mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"module-deps": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.1.0.tgz",
@ -2586,7 +2823,8 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"negotiator": {
"version": "0.6.2",
@ -2807,7 +3045,8 @@
"process-nextick-args": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true
},
"progress": {
"version": "2.0.3",
@ -2825,9 +3064,9 @@
}
},
"proxy-from-env": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
"integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4="
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"psl": {
"version": "1.1.29",
@ -2848,6 +3087,15 @@
"randombytes": "^2.0.1"
}
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
@ -2855,26 +3103,43 @@
"dev": true
},
"puppeteer": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz",
"integrity": "sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-3.0.0.tgz",
"integrity": "sha512-ArmIS8w+XhL4KGP05kxMousA9SFxmeirMkNNcVe5LjK4iGCbZ8qKnG4byuXMru7Ty7a9QwiMUIf80X+zmJuf2A==",
"requires": {
"@types/mime-types": "^2.1.0",
"debug": "^4.1.0",
"extract-zip": "^1.6.6",
"https-proxy-agent": "^2.2.1",
"extract-zip": "^2.0.0",
"https-proxy-agent": "^4.0.0",
"mime": "^2.0.3",
"mime-types": "^2.1.25",
"progress": "^2.0.1",
"proxy-from-env": "^1.0.0",
"rimraf": "^2.6.1",
"ws": "^6.1.0"
"rimraf": "^3.0.2",
"tar-fs": "^2.0.0",
"unbzip2-stream": "^1.3.3",
"ws": "^7.2.3"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"requires": {
"ms": "^2.1.1"
"ms": "2.1.2"
}
},
"mime-db": {
"version": "1.50.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz",
"integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A=="
},
"mime-types": {
"version": "2.1.33",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz",
"integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==",
"requires": {
"mime-db": "1.50.0"
}
},
"ms": {
@ -2961,6 +3226,7 @@
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@ -3075,11 +3341,11 @@
"dev": true
},
"rimraf": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"requires": {
"glob": "^7.0.5"
"glob": "^7.1.3"
}
},
"ripemd160": {
@ -3416,6 +3682,41 @@
"acorn-node": "^1.2.0"
}
},
"tar-fs": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"requires": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"requires": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
@ -3425,8 +3726,7 @@
"through": {
"version": "2.3.8",
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"through2": {
"version": "2.0.3",
@ -3537,7 +3837,8 @@
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
"umd": {
"version": "3.0.3",
@ -3545,6 +3846,15 @@
"integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==",
"dev": true
},
"unbzip2-stream": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
"integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
"requires": {
"buffer": "^5.2.1",
"through": "^2.3.8"
}
},
"undeclared-identifiers": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.2.tgz",
@ -3734,12 +4044,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
"requires": {
"async-limiter": "~1.0.0"
}
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz",
"integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w=="
},
"xtend": {
"version": "4.0.1",
@ -3748,11 +4055,12 @@
"dev": true
},
"yauzl": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
"integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
"requires": {
"fd-slicer": "~1.0.1"
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
}
}

View File

@ -31,7 +31,7 @@
"request": "^2.88.0"
},
"dependencies": {
"axe-core": "^3.3.0",
"puppeteer": "^1.20.0"
"axe-core": "3.5.3",
"puppeteer": "3.0.0"
}
}

View File

@ -1,7 +1,8 @@
let
sources = import ./nix/sources.nix;
nixpkgs = import sources.nixpkgs { };
niv = import sources.niv { };
system = if builtins.currentSystem == "aarch64-darwin" then "x86_64-darwin" else builtins.currentSystem;
nixpkgs = import sources.nixpkgs { inherit system; };
niv = nixpkgs.callPackage sources.niv { };
in with nixpkgs;
stdenv.mkDerivation {
name = "noredink-ui";

View File

@ -0,0 +1,74 @@
module InputErrorInternal exposing
( ErrorState, init
, setErrorIf, setErrorMessage
, getIsInError, getErrorMessage
)
{-|
@docs ErrorState, init
@docs setErrorIf, setErrorMessage
@docs getIsInError, getErrorMessage
-}
{-| -}
type ErrorState
= NoError
| Error { message : Maybe String }
{-| -}
init : ErrorState
init =
NoError
{-| -}
setErrorIf : Bool -> { config | error : ErrorState } -> { config | error : ErrorState }
setErrorIf isInError_ config =
{ config
| error =
if isInError_ then
Error { message = Nothing }
else
NoError
}
{-| -}
setErrorMessage : Maybe String -> { config | error : ErrorState } -> { config | error : ErrorState }
setErrorMessage maybeMessage config =
{ config
| error =
case maybeMessage of
Nothing ->
NoError
Just message ->
Error { message = Just message }
}
{-| -}
getIsInError : ErrorState -> Bool
getIsInError error =
case error of
NoError ->
False
Error _ ->
True
{-| -}
getErrorMessage : ErrorState -> Maybe String
getErrorMessage error =
case error of
NoError ->
Nothing
Error { message } ->
message

View File

@ -0,0 +1,45 @@
module InputLabelInternal exposing (view)
{-|
@docs view
-}
import Accessibility.Styled as Html exposing (Html)
import Accessibility.Styled.Style as Accessibility
import Css
import Html.Styled.Attributes as Attributes
import InputErrorInternal exposing (ErrorState)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.InputStyles.V3 as InputStyles exposing (Theme)
{-| -}
view :
{ for : String, label : String, theme : Theme }
-> { config | error : ErrorState, noMarginTop : Bool, hideLabel : Bool }
-> Html msg
view { for, label, theme } config =
let
extraStyles =
if config.hideLabel then
Accessibility.invisible
else
[]
in
Html.label
([ Attributes.for for
, Attributes.css
[ InputStyles.label theme (InputErrorInternal.getIsInError config.error)
, if config.noMarginTop then
Css.top (Css.px -InputStyles.defaultMarginTop)
else
Css.batch []
]
]
++ extraStyles
)
[ Html.text label ]

View File

@ -4,7 +4,7 @@ module Nri.Ui.Button.V10 exposing
, icon, custom, css, nriDescription, testId, id
, onClick
, href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking
, small, medium, large
, small, medium, large, modal
, exactWidth, boundedWidth, unboundedWidth, fillContainerWidth
, primary, secondary, danger, premium
, enabled, unfulfilled, disabled, error, loading, success
@ -19,6 +19,7 @@ module Nri.Ui.Button.V10 exposing
- uses ClickableAttributes
- adds `nriDescription`, `testId`, and `id` helpers
- adds `modal` helper, an alias for `large` size
# Changes from V9:
@ -42,7 +43,7 @@ module Nri.Ui.Button.V10 exposing
## Sizing
@docs small, medium, large
@docs small, medium, large, modal
@docs exactWidth, boundedWidth, unboundedWidth, fillContainerWidth
@ -265,6 +266,13 @@ large =
set (\attributes -> { attributes | size = Large })
{-| Alias for Button.large
-}
modal : Attribute msg
modal =
large
-- BUTTON WIDTH

View File

@ -2,7 +2,7 @@ module Nri.Ui.ClickableText.V3 exposing
( button
, link
, Attribute
, small, medium, large
, small, medium, large, modal
, onClick
, href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking
, icon
@ -19,6 +19,7 @@ module Nri.Ui.ClickableText.V3 exposing
- removes underline on hover and recolors to azureDark
- removes bottom border
- adds `nriDescription`, `testId`, and `id` helpers
- adds `modal` helper, for use in modal footers, same as applying large and Css.marginTop (Css.px 15)
# Changes from V2
@ -54,7 +55,7 @@ HTML `<a>` elements and are created here with `*Link` functions.
## Sizing
@docs small, medium, large
@docs small, medium, large, modal
## Behavior
@ -101,6 +102,19 @@ large =
set (\attributes -> { attributes | size = Large })
{-| For use in Modal footers (adds `large` and `Css.marginTop (Css.px 15)`)
-}
modal : Attribute msg
modal =
set
(\attributes ->
{ attributes
| size = Large
, customStyles = List.append attributes.customStyles [ Css.marginTop (Css.px 15) ]
}
)
type Size
= Small
| Medium

View File

@ -17,7 +17,7 @@ import Html.Styled.Attributes
import Nri.Ui
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.MediaQuery.V1 exposing (mobile)
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
{-| -}
@ -197,13 +197,15 @@ interactableWithLabel label content =
"label"
labelStyles
[]
[ Text.smallBody []
[ div
[ Html.Styled.Attributes.css
[ margin (px 7)
[ Text.smallBody
[ Text.html
[ div
[ Html.Styled.Attributes.css
[ margin (px 7)
]
]
[ text label
]
]
[ text label
]
]
]

View File

@ -54,7 +54,7 @@ import Nri.Ui
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
import Nri.Ui.MediaQuery.V1 exposing (mobile)
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
{-| -}

View File

@ -1,6 +1,6 @@
module Nri.Ui.InputStyles.V3 exposing
( label, Theme(..), input
, inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight
, inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight, defaultMarginTop
)
{-| InputStyles used by the TextInput and TextArea widgets.
@ -10,11 +10,13 @@ module Nri.Ui.InputStyles.V3 exposing
## Shared hardcoded values
@docs inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight
@docs inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight, defaultMarginTop
## Changelog
- patch: expose defaultMarginTop
- V3: add UserGenerated
-}
@ -108,6 +110,12 @@ label theme inError =
]
{-| -}
defaultMarginTop : Float
defaultMarginTop =
9
{-| In order to use these styles in an input module, you will need to add the class "override-sass-styles". This is because sass styles in the monolith have higher precendence than the class styles here.
-}
input : Theme -> Bool -> Style
@ -127,7 +135,7 @@ input theme isInError =
, display inlineBlock
, verticalAlign top
, marginBottom zero
, marginTop (px 9)
, marginTop (px defaultMarginTop)
, boxShadow6 inset zero (px 3) zero zero gray92
, property "transition" "border-color 0.4s ease"
, boxSizing borderBox

View File

@ -23,7 +23,7 @@ import Nri.Ui.Html.V3 exposing (viewIf)
{-| The default page information is for the button
which will direct the user back to the main page of
the SPA. Specify it's name and the message which will
the SPA. Specify its name and the message which will
navigate to the page.
-}
type alias DefaultPage msg =

434
src/Nri/Ui/Select/V8.elm Normal file
View File

@ -0,0 +1,434 @@
module Nri.Ui.Select.V8 exposing
( view, generateId
, Choice, choices
, value
, Attribute, defaultDisplayText
, hiddenLabel, visibleLabel
, errorIf, errorMessage
, custom, nriDescription, id, testId
, containerCss, noMargin
)
{-| Build a select input with a label, optional guidance, and error messaging.
# Changes from V7
- view adds a label
- adds standard custom, nriDescription, etc. attributes
- switches to a list-based attribute API from a record-based API
@docs view, generateId
### Input types
@docs Choice, choices
### Input content
@docs value
### Attributes
@docs Attribute, defaultDisplayText
@docs hiddenLabel, visibleLabel
@docs errorIf, errorMessage
@docs custom, nriDescription, id, testId
@docs containerCss, noMargin
-}
import Accessibility.Styled as Html exposing (Html)
import Css
import Dict
import Html.Styled.Attributes as Attributes exposing (css)
import Html.Styled.Events as Events
import InputErrorInternal exposing (ErrorState)
import InputLabelInternal
import Json.Decode exposing (Decoder)
import Nri.Ui
import Nri.Ui.Colors.Extra as ColorsExtra
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.CssVendorPrefix.V1 as VendorPrefixed
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Html.Attributes.V2 as Extra
import Nri.Ui.Html.V3 exposing (viewJust)
import Nri.Ui.InputStyles.V3 as InputStyles
import Nri.Ui.Message.V3 as Message
import Nri.Ui.Util
import SolidColor
{-| -}
defaultDisplayText : String -> Attribute value
defaultDisplayText text =
Attribute (\config -> { config | defaultDisplayText = Just text })
{-| -}
value : Maybe value -> Attribute value
value value_ =
Attribute (\config -> { config | value = value_ })
{-| Sets whether or not the field will be highlighted as having a validation error.
If you have an error message to display, use `errorMessage` instead.
-}
errorIf : Bool -> Attribute value
errorIf =
Attribute << InputErrorInternal.setErrorIf
{-| If `Just`, the field will be highlighted as having a validation error,
and the given error message will be shown.
-}
errorMessage : Maybe String -> Attribute value
errorMessage =
Attribute << InputErrorInternal.setErrorMessage
{-| Hides the visible label. (There will still be an invisible label for screen readers.)
-}
hiddenLabel : Attribute value
hiddenLabel =
Attribute (\config -> { config | hideLabel = True })
{-| Default behavior.
-}
visibleLabel : Attribute value
visibleLabel =
Attribute (\config -> { config | hideLabel = False })
{-| Set a custom ID for this text input and label. If you don't set this,
we'll automatically generate one from the label you pass in, but this can
cause problems if you have more than one text input with the same label on
the page. Use this to be more specific and avoid issues with duplicate IDs!
-}
id : String -> Attribute value
id id_ =
Attribute (\config -> { config | id = Just id_ })
{-| Use this helper to add custom attributes.
Do NOT use this helper to add css styles, as they may not be applied the way
you want/expect if underlying styles change.
Instead, please use the `css` helper.
-}
custom : List (Html.Attribute Never) -> Attribute value
custom attributes =
Attribute (\config -> { config | custom = config.custom ++ attributes })
{-| -}
nriDescription : String -> Attribute value
nriDescription description =
custom [ Extra.nriDescription description ]
{-| -}
testId : String -> Attribute value
testId id_ =
custom [ Extra.testId id_ ]
{-| Adds CSS to the element containing the input.
-}
containerCss : List Css.Style -> Attribute value
containerCss styles =
Attribute <|
\config -> { config | containerCss = config.containerCss ++ styles }
{-| Remove default spacing from the Input.
-}
noMargin : Bool -> Attribute value
noMargin removeMargin =
Attribute <| \config -> { config | noMarginTop = removeMargin }
{-| A single possible choice.
-}
type alias Choice value =
{ label : String, value : value }
{-| -}
choices : (value -> String) -> List (Choice value) -> Attribute value
choices valueToString choices_ =
Attribute
(\config ->
{ config
| valueToString = Just valueToString
, choices = choices_
}
)
{-| Customizations for the Select.
-}
type Attribute value
= Attribute (Config value -> Config value)
applyConfig : List (Attribute value) -> Config value
applyConfig attributes =
List.foldl (\(Attribute update) config -> update config)
defaultConfig
attributes
type alias Config value =
{ id : Maybe String
, value : Maybe value
, choices : List (Choice value)
, valueToString : Maybe (value -> String)
, defaultDisplayText : Maybe String
, error : ErrorState
, hideLabel : Bool
, noMarginTop : Bool
, containerCss : List Css.Style
, custom : List (Html.Attribute Never)
}
defaultConfig : Config value
defaultConfig =
{ id = Nothing
, value = Nothing
, choices = []
, valueToString = Nothing
, defaultDisplayText = Nothing
, error = InputErrorInternal.init
, hideLabel = False
, noMarginTop = False
, containerCss = []
, custom = []
}
{-| -}
view : String -> List (Attribute a) -> Html a
view label attributes =
let
config =
applyConfig attributes
isInError_ =
InputErrorInternal.getIsInError config.error
id_ =
Maybe.withDefault (generateId label) config.id
in
Html.div
[ css
([ Css.position Css.relative
, if config.noMarginTop then
Css.batch []
else
Css.paddingTop (Css.px InputStyles.defaultMarginTop)
]
++ config.containerCss
)
]
[ InputLabelInternal.view
{ for = id_
, label = label
, theme = InputStyles.Standard
}
config
, viewSelect
{ choices = config.choices
, current = config.value
, id = id_
, custom = config.custom
, valueToString = config.valueToString
, defaultDisplayText = config.defaultDisplayText
, isInError = isInError_
}
, viewJust
(\m ->
Message.view
[ Message.tiny
, Message.error
, Message.plaintext m
, Message.alertRole
]
)
(InputErrorInternal.getErrorMessage config.error)
]
viewSelect :
{ choices : List (Choice a)
, current : Maybe a
, id : String
, valueToString : Maybe (a -> String)
, defaultDisplayText : Maybe String
, isInError : Bool
, custom : List (Html.Attribute Never)
}
-> Html a
viewSelect config =
let
stringChoices =
case config.valueToString of
Just valueToString ->
List.map
(\choice ->
{ label = choice.label
, idAndValue = generateId (valueToString choice.value)
, value = choice.value
}
)
config.choices
Nothing ->
[]
valueLookup =
stringChoices
|> List.map (\x -> ( x.idAndValue, x.value ))
|> Dict.fromList
decodeValue string =
Dict.get string valueLookup
|> Maybe.map Json.Decode.succeed
|> Maybe.withDefault
-- At present, elm/virtual-dom throws this failure away.
(Json.Decode.fail
("Nri.Select: could not decode the value: "
++ string
++ "\nexpected one of: "
++ String.join ", " (Dict.keys valueLookup)
)
)
onSelectHandler =
Events.on "change" (Events.targetValue |> Json.Decode.andThen decodeValue)
defaultOption =
config.defaultDisplayText
|> Maybe.map (viewDefaultChoice config.current >> List.singleton)
|> Maybe.withDefault []
currentVal =
if config.current == Nothing && config.defaultDisplayText == Nothing then
config.choices
|> List.head
|> Maybe.map .value
else
config.current
in
stringChoices
|> List.map (viewChoice currentVal)
|> (++) defaultOption
|> Nri.Ui.styled Html.select
"nri-select-menu"
[ -- border
Css.border3 (Css.px 1)
Css.solid
(if config.isInError then
Colors.purple
else
Colors.gray75
)
, Css.borderBottomWidth (Css.px 3)
, Css.borderRadius (Css.px 8)
, Css.focus [ Css.borderColor Colors.azure ]
-- Font and color
, Css.color Colors.gray20
, Fonts.baseFont
, Css.fontSize (Css.px 15)
, Css.fontWeight (Css.int 600)
, Css.textOverflow Css.ellipsis
, Css.overflow Css.hidden
, Css.whiteSpace Css.noWrap
-- Interaction
, Css.cursor Css.pointer
-- Size and spacing
, Css.height (Css.px 45)
, Css.width (Css.pct 100)
, Css.paddingLeft (Css.px 15)
, Css.paddingRight (Css.px 30)
-- Icons
, selectArrowsCss
]
(onSelectHandler
:: Attributes.id config.id
:: List.map (Attributes.map never) config.custom
)
viewDefaultChoice : Maybe a -> String -> Html a
viewDefaultChoice current displayText =
Html.option
[ Attributes.selected (current == Nothing)
, Attributes.disabled True
]
[ Html.text displayText ]
viewChoice : Maybe a -> { value : a, idAndValue : String, label : String } -> Html a
viewChoice current choice =
let
isSelected =
current
|> Maybe.map ((==) choice.value)
|> Maybe.withDefault False
in
Html.option
[ Attributes.id choice.idAndValue
, Attributes.value choice.idAndValue
, Attributes.selected isSelected
]
[ Html.text choice.label ]
{-| Pass in the label to generate the default DOM element id used by a `Select.view` with the given label.
-}
generateId : String -> String
generateId x =
"nri-select-" ++ Nri.Ui.Util.dashify (Nri.Ui.Util.removePunctuation x)
selectArrowsCss : Css.Style
selectArrowsCss =
let
color =
SolidColor.toRGBString (ColorsExtra.fromCssColor Colors.azure)
in
Css.batch
[ """<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12px" height="16px" viewBox="0 0 12 16"><g fill=" """
++ color
++ """ "><path d="M2.10847,9.341803 C1.65347,8.886103 0.91427,8.886103 0.45857,9.341803 C0.23107,9.570003 0.11697,9.868203 0.11697,10.167103 C0.11697,10.465303 0.23107,10.763503 0.45857,10.991703 L5.12547,15.657903 C5.57977,16.114303 6.31897,16.114303 6.77537,15.657903 L11.44157,10.991703 C11.89727,10.536003 11.89727,9.797503 11.44157,9.341803 C10.98657,8.886103 10.24667,8.886103 9.79167,9.341803 L5.95007,13.182703 L2.10847,9.341803 Z"/><path d="M1.991556,6.658179 C1.536659,7.11394 0.797279,7.11394 0.3416911,6.658179 C0.1140698,6.43004 0,6.13173 0,5.83325 C0,5.53476 0.1140698,5.23645 0.3416911,5.00831 L5.008185,0.34182 C5.463081,-0.11394 6.202461,-0.11394 6.65805,0.34182 L11.32454,5.00831 C11.78031,5.4639 11.78031,6.202592 11.32454,6.658179 C10.86965,7.11394 10.13027,7.11394 9.674679,6.658179 L5.833118,2.81679 L1.991556,6.658179 Z"/></g></svg> """
|> urlUtf8
|> Css.property "background"
, Css.backgroundColor Colors.white
-- "appearance: none" removes the default dropdown arrows
, VendorPrefixed.property "appearance" "none"
, Css.backgroundRepeat Css.noRepeat
, Css.property "background-position" "center right -20px"
, Css.backgroundOrigin Css.contentBox
]
urlUtf8 : String -> String
urlUtf8 content =
"""url('data:image/svg+xml;utf8,""" ++ content ++ """')"""

View File

@ -1,8 +1,8 @@
module Nri.Ui.Switch.V1 exposing (view, Attribute, onSwitch, disabled, id, label)
module Nri.Ui.Switch.V1 exposing (view, Attribute, onSwitch, disabled, id, label, custom)
{-|
@docs view, Attribute, onSwitch, disabled, id, label
@docs view, Attribute, onSwitch, disabled, id, label, custom
-}
@ -27,6 +27,7 @@ type Attribute msg
| Id String
| Label (Html msg)
| Disabled
| Custom (List (Html.Attribute Never))
{-| Specify what happens when the switch is toggled.
@ -63,10 +64,18 @@ label =
Label
{-| Pass custom attributes through to be attached to the underlying input.
-}
custom : List (Html.Attribute Never) -> Attribute msg
custom =
Custom
type alias Config msg =
{ onSwitch : Maybe (Bool -> msg)
, id : String
, label : Maybe (Html msg)
, attributes : List (Html.Attribute Never)
}
@ -75,6 +84,7 @@ defaultConfig =
{ onSwitch = Nothing
, id = "nri-ui-switch-with-default-id"
, label = Nothing
, attributes = []
}
@ -93,6 +103,9 @@ customize attr config =
Label label_ ->
{ config | label = Just label_ }
Custom custom_ ->
{ config | attributes = custom_ }
{-| Render a switch. The boolean here indicates whether the switch is on
or not.
@ -133,6 +146,7 @@ view attrs isOn =
{ id = config.id
, onCheck = config.onSwitch
, checked = isOn
, attributes = config.attributes
}
, Nri.Ui.Svg.V1.toHtml
(viewSwitch
@ -162,26 +176,29 @@ viewCheckbox :
{ id : String
, onCheck : Maybe (Bool -> msg)
, checked : Bool
, attributes : List (Html.Attribute Never)
}
-> Html msg
viewCheckbox config =
Html.checkbox config.id
(Just config.checked)
[ Attributes.id config.id
, Attributes.css
([ Attributes.id config.id
, Attributes.css
[ Css.position Css.absolute
, Css.top (Css.px 10)
, Css.left (Css.px 10)
, Css.zIndex (Css.int 0)
, Css.opacity (Css.num 0)
]
, case config.onCheck of
, case config.onCheck of
Just onCheck ->
Events.onCheck onCheck
Nothing ->
Widget.disabled True
]
]
++ List.map (Attributes.map never) config.attributes
)
viewSwitch :

346
src/Nri/Ui/Text/V6.elm Normal file
View File

@ -0,0 +1,346 @@
module Nri.Ui.Text.V6 exposing
( caption, mediumBody, mediumBodyGray, smallBody, smallBodyGray
, ugMediumBody, ugSmallBody
, plaintext, markdown, html
, Attribute, noBreak, css, id, custom
, nriDescription, testId
)
{-| Changes from V5:
- adds helpers: `custom`, `nriDescription`,`testId`,`id`
- instead of view helpers that take HTML, offer attribute helpers supporting plaintext, markdown, and html content
- :skull: remove noWidow, which is not used
- noBreak now takes a bool
## Understanding spacing
- All text styles have a specific line-height. This is set so that when text in the given style
is long enough to wrap, the spacing between wrapped lines looks good.
- No text styles have padding.
- **Paragraph styles** only have bottom margin, but with **:last-child bottom margin set to zero**.
This bottom margin is set to look good when multiple paragraphs of the same style follow one another.
- If you want content after the paragraph and don't want the margin, put the paragraph in a `div` so that it will be the last-child, which will get rid of the bottom margin.
- **User-authored content blocks** preserve line breaks and do not have margin.
## Headings
You're in the wrong place! Headings live in Nri.Ui.Heading.V2.
## Paragraph styles
@docs caption, mediumBody, mediumBodyGray, smallBody, smallBodyGray
## User-authored content blocks:
@docs ugMediumBody, ugSmallBody
# Content
@docs plaintext, markdown, html
## Customizations
@docs Attribute, noBreak, css, id, custom
@docs nriDescription, testId
-}
import Accessibility.Styled as Html exposing (..)
import Css exposing (..)
import Css.Global exposing (a, descendants)
import Html.Styled.Attributes as Attributes
import Markdown
import Nri.Ui.Colors.V1 exposing (..)
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
{-| -}
type Attribute msg
= Attribute (Settings msg -> Settings msg)
type alias Settings msg =
{ noBreak : Bool
, styles : List Css.Style
, customAttributes : List (Html.Attribute Never)
, content : List (Html msg)
}
defaultSettings : Settings msg
defaultSettings =
{ noBreak = False
, styles = []
, customAttributes = []
, content = []
}
buildSettings : List (Attribute msg) -> Settings msg
buildSettings =
List.foldl (\(Attribute f) acc -> f acc) defaultSettings
{-| Pass True to prevent text from ever wrapping.
The default Text behavior is `noBreak False`, which means content will wrap.
-}
noBreak : Bool -> Attribute msg
noBreak noBreak_ =
Attribute (\config -> { config | noBreak = noBreak_ })
{-| Use this helper to add custom attributes.
Do NOT use this helper to add css styles, as they may not be applied the way
you want/expect if underlying styles change.
Instead, please use the `css` helper.
-}
custom : List (Html.Attribute Never) -> Attribute msg
custom attributes =
Attribute <|
\config ->
{ config
| customAttributes = List.append config.customAttributes attributes
}
{-| -}
nriDescription : String -> Attribute msg
nriDescription description =
custom [ ExtraAttributes.nriDescription description ]
{-| -}
testId : String -> Attribute msg
testId id_ =
custom [ ExtraAttributes.testId id_ ]
{-| -}
id : String -> Attribute msg
id id_ =
custom [ Attributes.id id_ ]
{-| Add some custom CSS to the text. If you find yourself using this a lot,
please add a stricter attribute to noredink-ui!
-}
css : List Style -> Attribute msg
css styles =
Attribute (\config -> { config | styles = config.styles ++ styles })
{-| -}
view : List (Attribute msg) -> Html msg
view attributes =
let
settings : Settings msg
settings =
buildSettings attributes
in
p
(Attributes.css
[ if settings.noBreak then
whiteSpace noWrap
else
batch []
, batch settings.styles
]
:: settings.customAttributes
)
settings.content
{-| This is some medium body copy.
-}
mediumBody : List (Attribute msg) -> Html msg
mediumBody attributes =
view
(css
(paragraphStyles
{ font = Fonts.baseFont
, color = gray20
, size = 18
, lineHeight = 28
, weight = 400
, margin = 10
}
)
:: attributes
)
{-| `mediumBody`, but with a lighter gray color than the default.
-}
mediumBodyGray : List (Attribute msg) -> Html msg
mediumBodyGray attributes =
mediumBody (css [ Css.color gray45 ] :: attributes)
{-| This is some small body copy.
-}
smallBody : List (Attribute msg) -> Html msg
smallBody attributes =
view
(css
(paragraphStyles
{ font = Fonts.baseFont
, color = gray20
, size = 15
, lineHeight = 23
, weight = 400
, margin = 7
}
)
:: attributes
)
{-| This is some small body copy but it's gray.
-}
smallBodyGray : List (Attribute msg) -> Html msg
smallBodyGray attributes =
view
(css
(paragraphStyles
{ font = Fonts.baseFont
, color = gray45
, size = 15
, lineHeight = 23
, weight = 400
, margin = 7
}
)
:: attributes
)
paragraphStyles :
{ color : Color
, font : Style
, lineHeight : Float
, margin : Float
, size : Float
, weight : Int
}
-> List Css.Style
paragraphStyles config =
[ config.font
, fontSize (px config.size)
, color config.color
, lineHeight (px config.lineHeight)
, fontWeight (int config.weight)
, padding zero
, textAlign left
, margin4 (px 0) (px 0) (px config.margin) (px 0)
, Css.Global.descendants
[ Css.Global.a
[ textDecoration none
, color azure
, borderBottom3 (px 1) solid azure
, visited
[ color azure ]
]
]
, lastChild
[ margin zero
]
]
{-| This is a little note or caption.
-}
caption : List (Attribute msg) -> Html msg
caption attributes =
view
(css
(paragraphStyles
{ font = Fonts.baseFont
, color = gray45
, size = 13
, lineHeight = 18
, weight = 400
, margin = 5
}
)
:: attributes
)
{-| User-generated text.
-}
ugMediumBody : List (Attribute msg) -> Html msg
ugMediumBody attributes =
view
(css
(whiteSpace preLine
:: paragraphStyles
{ font = Fonts.quizFont
, color = gray20
, size = 18
, lineHeight = 30
, weight = 400
, margin = 0
}
)
:: attributes
)
{-| User-generated text.
-}
ugSmallBody : List (Attribute msg) -> Html msg
ugSmallBody attributes =
view
(css
(whiteSpace preLine
:: paragraphStyles
{ font = Fonts.quizFont
, color = gray20
, size = 16
, lineHeight = 25
, weight = 400
, margin = 0
}
)
:: attributes
)
{-| Provide a plain-text string.
-}
plaintext : String -> Attribute msg
plaintext content =
Attribute <| \config -> { config | content = [ text content ] }
{-| Provide a string that will be rendered as markdown.
-}
markdown : String -> Attribute msg
markdown content =
Attribute <|
\config ->
{ config
| content =
Markdown.toHtml Nothing content
|> List.map fromUnstyled
}
{-| Provide a list of custom HTML.
-}
html : List (Html msg) -> Attribute msg
html content =
Attribute <| \config -> { config | content = content }

801
src/Nri/Ui/TextInput/V7.elm Normal file
View File

@ -0,0 +1,801 @@
module Nri.Ui.TextInput.V7 exposing
( view, generateId
, number, float, text, newPassword, currentPassword, email, search
, value, map
, onFocus, onBlur, onEnter
, Attribute, placeholder, autofocus
, hiddenLabel, visibleLabel
, css, custom, nriDescription, id, testId, noMargin
, disabled, loading, errorIf, errorMessage, guidance
, writing
)
{-|
# Changes from V6
- custom takes a list of attributes and appends them to the end of the previous attributes, instead of prepending a single attr.
- change `view` API so it only takes a list of attributes (meaning the value and input type are now passed in as attributes)
- make the search icon and reset pattern the default for `search`
- add "Show password" and "Hide password" as default behavior for `password` inputs
- split password into `newPassword` and `currentPassword` to fix the autocomplete behavior
@docs view, generateId
### Input types
@docs number, float, text, newPassword, currentPassword, email, search
### Input content
@docs value, map
### Event handlers
@docs onFocus, onBlur, onEnter
### Attributes
@docs Attribute, placeholder, autofocus
@docs hiddenLabel, visibleLabel
@docs css, custom, nriDescription, id, testId, noMargin
@docs disabled, loading, errorIf, errorMessage, guidance
@docs writing
-}
import Accessibility.Styled.Aria as Aria
import Accessibility.Styled.Style as Accessibility
import Css exposing (center, num, position, px, relative, textAlign)
import Css.Global
import Html.Styled as Html exposing (..)
import Html.Styled.Attributes as Attributes exposing (..)
import Html.Styled.Events as Events
import InputErrorInternal exposing (ErrorState)
import InputLabelInternal
import Keyboard.Event exposing (KeyboardEvent)
import Nri.Ui.ClickableSvg.V2 as ClickableSvg
import Nri.Ui.ClickableText.V3 as ClickableText
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Html.Attributes.V2 as Extra
import Nri.Ui.Html.V3 exposing (viewJust)
import Nri.Ui.InputStyles.V3 as InputStyles exposing (defaultMarginTop)
import Nri.Ui.Message.V3 as Message
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Text.V6 as Text
import Nri.Ui.UiIcon.V1 as UiIcon
import Nri.Ui.Util exposing (dashify)
{-| An input that allows text entry
-}
text : (String -> msg) -> Attribute String msg
text onInput_ =
Attribute
{ emptyEventsAndValues
| toString = Just identity
, fromString = Just identity
, onInput = Just (identity >> onInput_)
}
(\config ->
{ config
| fieldType = Just "text"
, inputMode = Nothing
, autocomplete = Nothing
}
)
{-| An input that allows integer entry
-}
number : (Maybe Int -> msg) -> Attribute (Maybe Int) msg
number onInput_ =
Attribute
{ emptyEventsAndValues
| toString = Just (Maybe.map String.fromInt >> Maybe.withDefault "")
, fromString = Just String.toInt
, onInput = Just (String.toInt >> onInput_)
}
(\config ->
{ config
| fieldType = Just "number"
, inputMode = Nothing
, autocomplete = Nothing
}
)
{-| An input that allows float entry
-}
float : (Maybe Float -> msg) -> Attribute (Maybe Float) msg
float onInput_ =
Attribute
{ emptyEventsAndValues
| toString = Just (Maybe.map String.fromFloat >> Maybe.withDefault "")
, fromString = Just String.toFloat
, onInput = Just (String.toFloat >> onInput_)
}
(\config ->
{ config
| fieldType = Just "number"
, inputMode = Nothing
, autocomplete = Nothing
}
)
{-| An input that allows password entry with autocomplete value "new-password"
If the user types at least one character into the input box, a
floating control "Show password" will appear. When clicked, the
input type will change from "password" to "text", in order
to enable the user to check what they've typed.
-}
newPassword :
{ onInput : String -> msg
, showPassword : Bool
, setShowPassword : Bool -> msg
}
-> Attribute String msg
newPassword =
password "new-password"
{-| An input that allows password entry with autocomplete value "current-password"
If the user types at least one character into the input box, a
floating control "Show password" will appear. When clicked, the
input type will change from "password" to "text", in order
to enable the user to check what they've typed.
-}
currentPassword :
{ onInput : String -> msg
, showPassword : Bool
, setShowPassword : Bool -> msg
}
-> Attribute String msg
currentPassword =
password "current-password"
password :
String
->
{ onInput : String -> msg
, showPassword : Bool
, setShowPassword : Bool -> msg
}
-> Attribute String msg
password autocomplete settings =
Attribute
{ emptyEventsAndValues
| toString = Just identity
, fromString = Just identity
, onInput = Just settings.onInput
, floatingContent =
Just <|
viewPasswordFloatingContent
(if settings.showPassword then
"Hide password"
else
"Show password"
)
(settings.setShowPassword (not settings.showPassword))
}
(\config ->
{ config
| fieldType =
Just <|
if settings.showPassword then
"text"
else
"password"
, inputMode = Nothing
, autocomplete = Just autocomplete
, inputCss = Css.paddingRight (Css.px 135) :: config.inputCss
}
)
{-| An input that is optimized for email entry
NOTE: this uses `inputmode="email"` so that mobile devices will use the email keyboard,
but not `type="email"` because that would enable browser-provided validation which is inconsistent and at odds
with our validation UI.
-}
email : (String -> msg) -> Attribute String msg
email onInput_ =
Attribute
{ emptyEventsAndValues
| toString = Just identity
, fromString = Just identity
, onInput = Just onInput_
}
(\config ->
{ config
| fieldType = Just "text"
, inputMode = Just "email"
, autocomplete = Just "email"
}
)
{-| An input with ["search" type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/search) specified.
-}
search : (String -> msg) -> Attribute String msg
search onInput_ =
Attribute
{ emptyEventsAndValues
| toString = Just identity
, fromString = Just identity
, floatingContent = Just viewSearchFloatingContent
, onInput = Just onInput_
}
(\config ->
{ config
| fieldType = Just "search"
, inputMode = Nothing
, autocomplete = Nothing
, inputCss = Css.paddingRight (Css.px 30) :: config.inputCss
}
)
{-| -}
value : value -> Attribute value msg
value value_ =
Attribute { emptyEventsAndValues | currentValue = Just value_ } identity
{-| If not explicit placeholder is given, the input label will be used as the placeholder.
-}
placeholder : String -> Attribute value msg
placeholder text_ =
Attribute emptyEventsAndValues <|
\config -> { config | placeholder = Just text_ }
{-| This disables the input
-}
disabled : Attribute value msg
disabled =
Attribute emptyEventsAndValues <|
\config -> { config | disabled = True }
{-| Use this while the form the input is a part of is being submitted.
-}
loading : Attribute value msg
loading =
Attribute emptyEventsAndValues <|
\config -> { config | loading = True }
{-| Sets whether or not the field will be highlighted as having a validation error.
-}
errorIf : Bool -> Attribute value msg
errorIf =
Attribute emptyEventsAndValues << InputErrorInternal.setErrorIf
{-| If `Just`, the field will be highlighted as having a validation error,
and the given error message will be shown.
-}
errorMessage : Maybe String -> Attribute value msg
errorMessage =
Attribute emptyEventsAndValues << InputErrorInternal.setErrorMessage
{-| A guidance message shows below the input, unless an error message is showing instead.
-}
guidance : String -> Attribute value msg
guidance message =
Attribute emptyEventsAndValues <|
\config -> { config | guidance = Just message }
{-| Hides the visible label. (There will still be an invisible label for screen readers.)
-}
hiddenLabel : Attribute value msg
hiddenLabel =
Attribute emptyEventsAndValues <|
\config -> { config | hideLabel = True }
{-| Default behavior.
-}
visibleLabel : Attribute value msg
visibleLabel =
Attribute emptyEventsAndValues <|
\config -> { config | hideLabel = False }
{-| Causes the TextInput to produce the given `msg` when the field is focused.
-}
onFocus : msg -> Attribute value msg
onFocus msg =
Attribute { emptyEventsAndValues | onFocus = Just msg } identity
{-| Causes the TextInput to produce the given `msg` when the field is blurred.
-}
onBlur : msg -> Attribute value msg
onBlur msg =
Attribute { emptyEventsAndValues | onBlur = Just msg } identity
{-| -}
onEnter : msg -> Attribute value msg
onEnter msg =
Attribute { emptyEventsAndValues | onEnter = Just msg } identity
{-| Sets the `autofocus` attribute of the resulting HTML input.
-}
autofocus : Attribute value msg
autofocus =
Attribute emptyEventsAndValues <|
\config -> { config | autofocus = True }
{-| Adds CSS to the element containing the input.
If you want to customize colors, borders, font sizes, etc, you should instead add to the TextInput API
to support what you need.
-}
css : List Css.Style -> Attribute value msg
css styles =
Attribute emptyEventsAndValues <|
\config -> { config | containerCss = config.containerCss ++ styles }
{-| Remove default spacing from the Input.
-}
noMargin : Bool -> Attribute value msg
noMargin removeMargin =
Attribute emptyEventsAndValues <| \config -> { config | noMarginTop = removeMargin }
{-| Use this helper to add custom attributes.
Do NOT use this helper to add css styles, as they may not be applied the way
you want/expect if underlying styles change.
Instead, please use the `css` helper.
-}
custom : List (Html.Attribute Never) -> Attribute value msg
custom attributes =
Attribute emptyEventsAndValues <|
\config -> { config | custom = config.custom ++ attributes }
{-| Set a custom ID for this text input and label. If you don't set this,
we'll automatically generate one from the label you pass in, but this can
cause problems if you have more than one text input with the same label on
the page. Use this to be more specific and avoid issues with duplicate IDs!
-}
id : String -> Attribute value msg
id id_ =
Attribute emptyEventsAndValues <|
\config -> { config | id = Just id_ }
{-| -}
nriDescription : String -> Attribute value msg
nriDescription description =
custom [ Extra.nriDescription description ]
{-| -}
testId : String -> Attribute value msg
testId id_ =
custom [ Extra.testId id_ ]
{-| Uses the "Writing" input style.
-}
writing : Attribute value msg
writing =
Attribute emptyEventsAndValues <|
\config -> { config | inputStyle = InputStyles.Writing }
{-| Customizations for the TextInput.
-}
type Attribute value msg
= Attribute (EventsAndValues value msg) (Config -> Config)
type alias EventsAndValues value msg =
{ currentValue : Maybe value
, toString : Maybe (value -> String)
, fromString : Maybe (String -> value)
, onInput : Maybe (String -> msg)
, onFocus : Maybe msg
, onBlur : Maybe msg
, onEnter : Maybe msg
, floatingContent : Maybe (FloatingContentConfig msg -> Html msg)
}
emptyEventsAndValues : EventsAndValues value msg
emptyEventsAndValues =
{ currentValue = Nothing
, toString = Nothing
, fromString = Nothing
, onFocus = Nothing
, onBlur = Nothing
, onEnter = Nothing
, onInput = Nothing
, floatingContent = Nothing
}
{-| -}
map : (a -> b) -> (b -> String) -> Attribute a msg -> Attribute b msg
map f toString (Attribute eventsAndValues configF) =
Attribute
{ currentValue = Maybe.map f eventsAndValues.currentValue
, toString = Just toString
, fromString = Maybe.map (\from -> from >> f) eventsAndValues.fromString
, onFocus = eventsAndValues.onFocus
, onBlur = eventsAndValues.onBlur
, onEnter = eventsAndValues.onEnter
, onInput = eventsAndValues.onInput
, floatingContent = eventsAndValues.floatingContent
}
configF
{-| This is private. The public API only exposes `Attribute`.
-}
type alias Config =
{ inputStyle : InputStyles.Theme
, inputCss : List Css.Style
, guidance : Maybe String
, error : ErrorState
, disabled : Bool
, loading : Bool
, hideLabel : Bool
, placeholder : Maybe String
, autofocus : Bool
, noMarginTop : Bool
, containerCss : List Css.Style
, id : Maybe String
, custom : List (Html.Attribute Never)
, fieldType : Maybe String
, inputMode : Maybe String
, autocomplete : Maybe String
}
emptyConfig : Config
emptyConfig =
{ inputStyle = InputStyles.Standard
, inputCss = []
, guidance = Nothing
, error = InputErrorInternal.init
, disabled = False
, loading = False
, hideLabel = False
, placeholder = Nothing
, autofocus = False
, id = Nothing
, noMarginTop = False
, containerCss = []
, custom = []
, fieldType = Nothing
, inputMode = Nothing
, autocomplete = Nothing
}
applyConfig : List (Attribute value msg) -> Config
applyConfig attributes =
List.foldl (\(Attribute _ update) config -> update config)
emptyConfig
attributes
orExisting : (acc -> Maybe a) -> acc -> acc -> Maybe a
orExisting f new previous =
case f previous of
Just just ->
Just just
Nothing ->
f new
applyEvents : List (Attribute value msg) -> EventsAndValues value msg
applyEvents =
List.foldl
(\(Attribute eventsAndValues _) existing ->
{ currentValue = orExisting .currentValue eventsAndValues existing
, toString = orExisting .toString eventsAndValues existing
, fromString = orExisting .fromString eventsAndValues existing
, onFocus = orExisting .onFocus eventsAndValues existing
, onBlur = orExisting .onBlur eventsAndValues existing
, floatingContent = orExisting .floatingContent eventsAndValues existing
, onEnter = orExisting .onEnter eventsAndValues existing
, onInput = orExisting .onInput eventsAndValues existing
}
)
emptyEventsAndValues
{-| Render the TextInput as HTML.
-}
view : String -> List (Attribute value msg) -> Html msg
view label attributes =
let
config : Config
config =
applyConfig attributes
eventsAndValues : EventsAndValues value msg
eventsAndValues =
applyEvents attributes
idValue =
case config.id of
Just id_ ->
id_
Nothing ->
generateId label
placeholder_ =
config.placeholder
|> Maybe.withDefault label
isInError =
InputErrorInternal.getIsInError config.error
errorMessage_ =
InputErrorInternal.getErrorMessage config.error
( opacity, disabled_ ) =
case ( config.disabled, config.loading ) of
( False, False ) ->
( num 1, False )
( False, True ) ->
( num 0.5, True )
( True, _ ) ->
( num 0.4, True )
maybeStep =
case config.fieldType of
Just "number" ->
[ step "any" ]
_ ->
[]
maybeAttr attr maybeValue =
maybeValue
|> Maybe.map attr
|> Maybe.withDefault Extra.none
stringValue =
eventsAndValues.currentValue
|> Maybe.map2 identity eventsAndValues.toString
|> Maybe.withDefault ""
onEnter_ : msg -> Html.Attribute msg
onEnter_ msg =
(\event ->
case event.key of
Just "Enter" ->
Just msg
_ ->
Nothing
)
|> Keyboard.Event.considerKeyboardEvent
|> Events.on "keydown"
in
div
[ Attributes.css
(position relative
:: Css.opacity opacity
:: config.containerCss
)
]
[ input
(maybeStep
++ List.map (Attributes.map never) (List.reverse config.custom)
++ [ Attributes.id idValue
, case ( errorMessage_, config.guidance ) of
( Nothing, Just _ ) ->
Aria.describedBy [ idValue ++ "_guidance" ]
_ ->
Extra.none
, Attributes.css
[ InputStyles.input config.inputStyle isInError
, if config.inputStyle == InputStyles.Writing then
Css.Global.withClass "override-sass-styles"
[ textAlign center
, Css.height Css.auto
]
else
Css.Global.withClass "override-sass-styles"
[ Css.height (px 45)
]
, Css.pseudoElement "-webkit-search-cancel-button"
[ Css.display Css.none ]
, if config.noMarginTop then
Css.important (Css.marginTop Css.zero)
else
Css.batch []
, Css.batch config.inputCss |> Css.important
]
, Attributes.placeholder placeholder_
, Attributes.value stringValue
, Attributes.disabled disabled_
, maybeAttr Events.onInput eventsAndValues.onInput
, maybeAttr Events.onFocus eventsAndValues.onFocus
, maybeAttr Events.onBlur eventsAndValues.onBlur
, Attributes.autofocus config.autofocus
, maybeAttr type_ config.fieldType
, maybeAttr (attribute "inputmode") config.inputMode
, maybeAttr (attribute "autocomplete") config.autocomplete
, maybeAttr onEnter_ eventsAndValues.onEnter
, class "override-sass-styles"
, Attributes.attribute "aria-invalid" <|
if isInError then
"true"
else
"false"
]
)
[]
, InputLabelInternal.view
{ for = idValue
, label = label
, theme = config.inputStyle
}
config
, Maybe.map2
(\view_ onStringInput_ ->
view_
{ label = label
, stringValue = stringValue
, onInput = onStringInput_
, noMarginTop = config.noMarginTop
}
)
eventsAndValues.floatingContent
eventsAndValues.onInput
|> Maybe.withDefault (Html.text "")
, case ( errorMessage_, config.guidance ) of
( Just m, _ ) ->
Message.view
[ Message.tiny
, Message.error
, Message.plaintext m
, Message.alertRole
]
( _, Just guidanceMessage ) ->
Text.caption
[ Text.id (idValue ++ "_guidance")
, Text.plaintext guidanceMessage
, -- Match the vertical styles of the error message
Text.css
[ Css.paddingTop (Css.px 6)
, Css.paddingBottom (Css.px 8)
, Css.lineHeight (Css.px 23)
]
]
_ ->
Html.text ""
]
{-| Gives you the default DOM element id that will be used by a `TextInput.view` with the given label.
This is for use when you need the DOM element id for use in javascript (such as trigger an event to focus a particular text input)
-}
generateId : String -> String
generateId labelText =
"Nri-Ui-TextInput-" ++ dashify labelText
type alias FloatingContentConfig msg =
{ label : String
, stringValue : String
, onInput : String -> msg
, noMarginTop : Bool
}
viewSearchFloatingContent : FloatingContentConfig msg -> Html msg
viewSearchFloatingContent config =
if config.stringValue == "" then
searchIcon config
else
resetButton config
searchIcon : { settings | noMarginTop : Bool } -> Html msg
searchIcon config =
UiIcon.search
|> Svg.withWidth (Css.px 20)
|> Svg.withHeight (Css.px 20)
|> Svg.withColor Colors.gray75
|> Svg.withCss
[ Css.position Css.absolute
, Css.right (Css.px 10)
, if config.noMarginTop then
Css.top (Css.px (22 - defaultMarginTop))
else
Css.top (Css.px 22)
]
|> Svg.toHtml
resetButton : FloatingContentConfig msg -> Html msg
resetButton config =
ClickableSvg.button ("Reset " ++ config.label)
UiIcon.x
[ ClickableSvg.onClick (config.onInput "")
, ClickableSvg.exactWidth 14
, ClickableSvg.exactHeight 14
, ClickableSvg.css
[ Css.position Css.absolute
, Css.right (Css.px 10)
, if config.noMarginTop then
Css.top (Css.px (25 - defaultMarginTop))
else
Css.top (Css.px 25)
]
, ClickableSvg.custom [ Attributes.type_ "button" ]
]
viewPasswordFloatingContent : String -> msg -> FloatingContentConfig msg -> Html msg
viewPasswordFloatingContent label toggle config =
if config.stringValue == "" then
Html.text ""
else
-- TODO: consider using a "toggle" clickable text button,
-- a checkbox styled to look like a clickable text, or
-- adding additional aria attributes connecting this clickable
-- text to the password field.
ClickableText.button label
[ ClickableText.onClick toggle
, ClickableText.small
, ClickableText.css
[ Css.position Css.absolute
, Css.right (Css.px 15)
, if config.noMarginTop then
Css.top (Css.px (22 - defaultMarginTop))
else
Css.top (Css.px 22)
, Css.fontSize (Css.px 13)
]
, ClickableText.custom [ Attributes.type_ "button" ]
]

View File

@ -26,6 +26,7 @@ Post-release patches:
- mark customTriggerAttributes as deprecated
- add containerCss
- adds `nriDescription` and `testId`
- fix <https://github.com/NoRedInk/noredink-ui/issues/766>
Changes from V1:
@ -106,6 +107,7 @@ type Attribute msg
type alias Tooltip msg =
{ direction : Direction
, alignment : Alignment
, tail : Tail
, content : List (Html msg)
, attributes : List (Html.Attribute Never)
@ -126,7 +128,8 @@ buildAttributes =
defaultTooltip : Tooltip msg
defaultTooltip =
{ direction = OnTop
, tail = WithTail Middle
, alignment = Middle
, tail = WithTail
, content = []
, attributes = []
, containerStyles =
@ -160,7 +163,7 @@ html content =
type Tail
= WithTail Alignment
= WithTail
| WithoutTail
@ -173,10 +176,6 @@ type Alignment
{-| Makes it so that the tooltip does not have a tail!
This will center the tooltip relative to the trigger content, superseding any
custom alignment set by `alignStart` and `alignEnd`.
-}
withoutTail : Attribute msg
withoutTail =
@ -185,17 +184,7 @@ withoutTail =
withAligment : Alignment -> Attribute msg
withAligment alignment =
Attribute (\config -> { config | tail = WithTail alignment })
tailAlignment : Tail -> Alignment
tailAlignment tail =
case tail of
WithTail alignment ->
alignment
WithoutTail ->
Middle
Attribute (\config -> { config | alignment = alignment })
{-| Put the tail at the "start" of the tooltip.
@ -664,7 +653,7 @@ viewOpenTooltip tooltipId config =
Html.div
[ Attributes.css
[ Css.position Css.absolute
, positionTooltip config.direction (tailAlignment config.tail)
, positionTooltip config.direction config.alignment
, Css.boxSizing Css.borderBox
]
]
@ -684,7 +673,7 @@ viewOpenTooltip tooltipId config =
]
++ config.tooltipStyleOverrides
)
, pointerBox config.tail config.direction (tailAlignment config.tail)
, pointerBox config.tail config.direction config.alignment
-- We need to keep this animation in tests to make it pass: check out
-- the NoAnimations middleware. So if you change the name here, please
@ -771,7 +760,7 @@ pointerBox tail direction alignment =
, Css.border3 (Css.px 1) Css.solid Colors.navy
, positioning direction alignment
, case tail of
WithTail _ ->
WithTail ->
tailForDirection direction
WithoutTail ->

View File

@ -209,14 +209,15 @@ performance =
, Attributes.fill "currentcolor"
, Attributes.viewBox "0 0 30 30"
]
[ Svg.polygon [ Attributes.points "22.1,24.6 22.1,8.4 17.8,7.1 17.8,24.6 " ] []
, Svg.polygon [ Attributes.points "24.2,7.7 24.2,24.6 28.5,24.6 28.5,5 26.3,3.5 " ] []
, Svg.polygon [ Attributes.points "5,15.6 5,24.6 9.3,24.6 9.3,12.3 5.8,16.5 " ] []
, Svg.polygon [ Attributes.points "11.4,24.6 15.7,24.6 15.7,6.5 14.5,6.2 11.4,9.8 " ] []
, Svg.path [ Attributes.fill "none", Attributes.d "M33.6,26.9H30v1.2c0,1.6-0.4,1.8-1.8,1.8H1.8C0.4,30,0,29.7,0,28.2V1.9c0-1.4,0.3-1.8,1.8-1.8H3v-5.5h30.6V26.9z" ] []
, Svg.path [ Attributes.d "M3.1,26.9V3.2V0.1H1.8C0.3,0.1,0,0.5,0,1.9v26.2C0,29.7,0.4,30,1.8,30h26.3c1.5,0,1.8-0.3,1.8-1.8V27h-3.1H3.1V26.9z" ] []
, Svg.path [ Attributes.fill "none", Attributes.d "M-715-401V715H309V-401H-715z" ] []
, Svg.path [ Attributes.fill "none", Attributes.d "M-737.2-385.9v1116h1024v-1116L-737.2-385.9L-737.2-385.9z" ] []
[ Svg.path
[ Attributes.d "M2.575,22.5 L2.55333333,2.47096774 L2.55333333,5.68434189e-14 L1.53166667,5.68434189e-14 C0.275833333,5.68434189e-14 0,0.345967742 0,1.48225806 L0.0216666667,23.4887097 C0.0216666667,24.7185484 0.3275,24.9709677 1.55333333,24.9709677 L23.4891667,24.9709677 C24.7191667,24.9709677 25.0216667,24.7483871 25.0216667,23.4887097 L25.0216667,22.5 L22.4675,22.5 L2.575,22.5 Z" ]
[]
, Svg.rect
[ Attributes.x "4.224", Attributes.y "11.9331996", Attributes.width "3.55583333", Attributes.height "9.42432817", Attributes.rx "1" ]
[]
, Svg.rect [ Attributes.x "9.47510076", Attributes.y "5.31243234", Attributes.width "3.55583333", Attributes.height "16.0450955", Attributes.rx "1" ] []
, Svg.rect [ Attributes.x "14.7262015", Attributes.y "8.69585911", Attributes.width "3.55583333", Attributes.height "12.6616687", Attributes.rx "1" ] []
, Svg.rect [ Attributes.x "19.9773023", Attributes.y "3.19919812", Attributes.width "3.55583333", Attributes.height "18.1583297", Attributes.rx "1" ] []
]
|> Nri.Ui.Svg.V1.fromHtml

View File

@ -1,6 +1,8 @@
module CommonControls exposing (httpError)
module CommonControls exposing (exampleHtml, httpError, quickBrownFox, romeoAndJulietQuotation)
import Debug.Control as Control exposing (Control)
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes
import Http
@ -38,3 +40,42 @@ httpError =
)
)
]
quickBrownFox : String
quickBrownFox =
"The quick brown fox jumps over the lazy dog."
romeoAndJulietQuotation : String
romeoAndJulietQuotation =
"""
Two households, both alike in dignity,
In fair Verona, where we lay our scene,
From ancient grudge break to new mutiny,
Where civil blood makes civil hands unclean.
From forth the fatal loins of these two foes
A pair of star-crossd lovers take their life;
Whose misadventured piteous overthrows
Do with their death bury their parents strife.
The fearful passage of their death-markd love,
And the continuance of their parents rage,
Which, but their childrens end, nought could remove,
Is now the two hours traffic of our stage;
The which if you with patient ears attend,
What here shall miss, our toil shall strive to mend.
"""
exampleHtml : List (Html msg)
exampleHtml =
[ Html.text "This is a "
, Html.strong [] [ Html.text "bolded phrase" ]
, Html.text ". "
, Html.a
[ Attributes.href "http://www.noredink.com"
, Attributes.target "_blank"
]
[ Html.text quickBrownFox ]
, Html.text " When I stepped out, into the bright sunlight from the darkness of the movie house, I had only two things on my mind: Paul Newman, and a ride home."
]

View File

@ -1,15 +1,22 @@
module Example exposing (Example, view, wrapMsg, wrapState)
module Example exposing (Example, preview, view, wrapMsg, wrapState)
import Category exposing (Category)
import Css exposing (..)
import Css.Global exposing (a, descendants)
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes
import Html.Styled.Events as Events
import Html.Styled.Lazy as Lazy
import KeyboardSupport exposing (KeyboardSupport)
import Nri.Ui.Colors.V1 exposing (..)
import Nri.Ui.ClickableSvg.V2 as ClickableSvg
import Nri.Ui.ClickableText.V3 as ClickableText
import Nri.Ui.Colors.V1 as Colors exposing (..)
import Nri.Ui.Container.V2 as Container
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Html.Attributes.V2 as AttributeExtras exposing (targetBlank)
import Nri.Ui.UiIcon.V1 as UiIcon
import Routes exposing (Route)
type alias Example state msg =
@ -18,12 +25,18 @@ type alias Example state msg =
, state : state
, update : msg -> state -> ( state, Cmd msg )
, subscriptions : state -> Sub msg
, preview : List (Html Never)
, view : state -> List (Html msg)
, categories : List Category
, keyboardSupport : List KeyboardSupport
}
fullName : Example state msg -> String
fullName example =
"Nri.Ui." ++ example.name ++ ".V" ++ String.fromInt example.version
wrapMsg :
(msg -> msg2)
-> (msg2 -> Maybe msg)
@ -43,6 +56,7 @@ wrapMsg wrapMsg_ unwrapMsg example =
Nothing ->
( state, Cmd.none )
, subscriptions = \state -> Sub.map wrapMsg_ (example.subscriptions state)
, preview = example.preview
, view = \state -> List.map (Html.map wrapMsg_) (example.view state)
, categories = example.categories
, keyboardSupport = example.keyboardSupport
@ -71,6 +85,7 @@ wrapState wrapState_ unwrapState example =
unwrapState
>> Maybe.map example.subscriptions
>> Maybe.withDefault Sub.none
, preview = example.preview
, view =
unwrapState
>> Maybe.map example.view
@ -80,22 +95,77 @@ wrapState wrapState_ unwrapState example =
}
view : Example state msg -> Html msg
view =
Lazy.lazy view_
preview : (Route -> msg2) -> Example state msg -> Html msg2
preview navigate =
Lazy.lazy (preview_ navigate)
preview_ : (Route -> msg2) -> Example state msg -> Html msg2
preview_ navigate example =
Container.view
[ Container.gray
, Container.css
[ Css.flexBasis (Css.px 150)
, Css.hover
[ Css.backgroundColor Colors.glacier
, Css.cursor Css.pointer
]
]
, Container.custom [ Events.onClick (navigate (Routes.Doodad example.name)) ]
, Container.html
(ClickableText.link example.name
[ ClickableText.href (exampleHref example)
, ClickableText.css [ Css.marginBottom (Css.px 10) ]
]
:: [ Html.div
[ Attributes.css
[ Css.displayFlex
, Css.flexDirection Css.column
]
]
(List.map (Html.map never) example.preview)
]
)
]
view : Maybe Route -> Example state msg -> Html msg
view previousRoute example =
Container.view
[ Container.pillow
, Container.css
[ Css.position Css.relative
, Css.margin (Css.px 10)
, Css.minHeight (Css.calc (Css.vh 100) Css.minus (Css.px 20))
, Css.boxSizing Css.borderBox
]
, Container.html
[ Lazy.lazy view_ example
, ClickableSvg.link ("Close " ++ example.name ++ " example")
UiIcon.x
[ ClickableSvg.href
(Maybe.withDefault Routes.All previousRoute
|> Routes.toString
)
, ClickableSvg.small
, ClickableSvg.css
[ Css.position Css.absolute
, Css.top (Css.px 15)
, Css.right (Css.px 15)
]
]
]
]
view_ : Example state msg -> Html msg
view_ example =
let
fullName =
"Nri.Ui." ++ example.name ++ ".V" ++ String.fromInt example.version
in
Html.div
[ -- this class makes the axe accessibility checking output easier to parse
String.replace "." "-" example.name
|> (++) "module-example__"
|> Attributes.class
, Attributes.id (String.replace "." "-" example.name)
]
[ Html.div
[ Attributes.css
@ -107,52 +177,57 @@ view_ example =
, descendants [ Css.Global.a [ textDecoration none ] ]
]
]
[ Html.styled Html.h2
[ color navy
, fontSize (px 20)
, marginTop zero
, marginBottom zero
, Fonts.baseFont
]
[]
[ Html.a
[ Attributes.href ("#/doodad/" ++ example.name)
, Attributes.class "module-example__doodad-link"
, -- this data attribute is used to name the Percy screenshots
String.replace "." "-" example.name
|> Attributes.attribute "data-percy-name"
]
[ Html.text fullName ]
]
, String.replace "." "-" fullName
|> (++) "https://package.elm-lang.org/packages/NoRedInk/noredink-ui/latest/"
|> viewLink "Docs"
, String.replace "." "/" fullName
++ ".elm"
|> (++) "https://github.com/NoRedInk/noredink-ui/blob/master/src/"
|> viewLink "Source"
[ exampleLink example
, docsLink example
, srcLink example
]
, KeyboardSupport.view example.keyboardSupport
, Html.div
[ Attributes.css
[ padding (px 40)
, boxShadow5 zero (px 2) (px 4) zero (rgba 0 0 0 0.25)
, border3 (px 1) solid gray92
, borderRadius (px 20)
, margin3 (px 10) zero (px 40)
, Html.div [] (example.view example.state)
]
exampleHref : Example state msg -> String
exampleHref example =
Routes.toString (Routes.Doodad example.name)
exampleLink : Example state msg -> Html msg
exampleLink example =
Heading.h2 []
[ ClickableText.link (fullName example)
[ ClickableText.href (exampleHref example)
, ClickableText.large
, ClickableText.custom
[ -- this data attribute is used to name the Percy screenshots
String.replace "." "-" example.name
|> Attributes.attribute "data-percy-name"
]
]
(example.view example.state)
]
viewLink : String -> String -> Html msg
viewLink text href =
Html.a
([ Attributes.href href
, Attributes.css [ Css.display Css.block, marginLeft (px 20) ]
]
++ targetBlank
)
[ Html.text text
docsLink : Example state msg -> Html msg
docsLink example =
let
link =
"https://package.elm-lang.org/packages/NoRedInk/noredink-ui/latest/"
++ String.replace "." "-" (fullName example)
in
ClickableText.link "Docs"
[ ClickableText.linkExternal link
, ClickableText.css [ Css.marginLeft (Css.px 20) ]
]
srcLink : Example state msg -> Html msg
srcLink example =
let
link =
String.replace "." "/" (fullName example)
++ ".elm"
|> (++) "https://github.com/NoRedInk/noredink-ui/blob/master/src/"
in
ClickableText.link "Source"
[ ClickableText.linkExternal link
, ClickableText.css [ Css.marginLeft (Css.px 20) ]
]

View File

@ -25,6 +25,7 @@ import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Text.V6 as Text
import Nri.Ui.UiIcon.V1 as UiIcon
import Set exposing (Set)
import Task
@ -38,6 +39,21 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ -- faking a mini version of the Accordion component to give styleguide users a sense of what the
-- component might look like
Html.div []
[ Html.div [ css [ Css.displayFlex, Css.alignItems Css.center ] ]
[ defaultCaret False
, Text.smallBody [ Text.plaintext "Closed" ]
]
, Html.div [ css [ Css.displayFlex, Css.alignItems Css.center ] ]
[ defaultCaret True
, Text.smallBody [ Text.plaintext "Open" ]
]
, Text.caption [ Text.plaintext "Accordion content." ]
]
]
, view = view
, categories = [ Layout ]
, keyboardSupport =
@ -57,13 +73,14 @@ example =
}
defaultCaret : Bool -> Html msg
defaultCaret =
DisclosureIndicator.large [ Css.marginRight (Css.px 8) ]
{-| -}
view : State -> List (Html Msg)
view model =
let
defaultCaret =
DisclosureIndicator.large [ Css.marginRight (Css.px 8) ]
in
[ Heading.h3 [] [ Html.text "Accordion.view" ]
, Accordion.view
{ entries =

View File

@ -33,6 +33,21 @@ example =
, state = ()
, update = \_ state -> ( state, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview =
IconExamples.preview
[ AssignmentIcon.planningDiagnosticCircled
, AssignmentIcon.unitDiagnosticCircled
, AssignmentIcon.practiceCircled
, AssignmentIcon.quizCircled
, AssignmentIcon.quickWriteCircled
, AssignmentIcon.guidedDraftCircled
, AssignmentIcon.peerReviewCircled
, AssignmentIcon.selfReviewCircled
, AssignmentIcon.startPrimary
, AssignmentIcon.assessment
, AssignmentIcon.standards
, AssignmentIcon.writing
]
, view =
\_ ->
[ IconExamples.view "Diagnostic"

View File

@ -29,32 +29,39 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ Balloon.balloon
[ Balloon.onTop
, Balloon.navy
, Balloon.paddingPx 15
]
(text "This is a balloon.")
]
, view = view
}
{-| -}
type alias State =
Control Settings
type alias Settings =
{ copy : String
, theme : Maybe ( String, Balloon.Attribute )
, position : Maybe ( String, Balloon.Attribute )
, width : Maybe ( String, Balloon.Attribute )
, padding : Maybe ( String, Balloon.Attribute )
{ copy : Control String
, attributes : Control (List ( String, Balloon.Attribute ))
}
init : Control Settings
init : State
init =
Control.record Settings
|> Control.field "copy" (Control.string "Hello, world!")
|> Control.field "theme" (Control.maybe False themeOptions)
|> Control.field "position" (Control.maybe False positionOptions)
|> Control.field "width" (Control.maybe False widthOptions)
|> Control.field "padding" (Control.maybe False paddingOptions)
{ copy = Control.string "Hello, world!"
, attributes = controlAttributes
}
controlAttributes : Control (List ( String, Balloon.Attribute ))
controlAttributes =
ControlExtra.list
|> ControlExtra.optionalListItem "theme" themeOptions
|> ControlExtra.optionalListItem "position" positionOptions
|> ControlExtra.optionalListItem "width" widthOptions
|> ControlExtra.optionalListItem "padding" paddingOptions
themeOptions : Control ( String, Balloon.Attribute )
@ -103,14 +110,20 @@ paddingOptions =
{-| -}
type Msg
= SetDebugControlsState (Control Settings)
= SetCopy (Control String)
| SetAttributes (Control (List ( String, Balloon.Attribute )))
update : Msg -> State -> ( State, Cmd Msg )
update msg state =
case msg of
SetDebugControlsState newDebugControlsState ->
( newDebugControlsState
SetCopy copy ->
( { state | copy = copy }
, Cmd.none
)
SetAttributes attributes ->
( { state | attributes = attributes }
, Cmd.none
)
@ -118,26 +131,22 @@ update msg state =
view : State -> List (Html Msg)
view state =
let
settings =
Control.currentValue state
copy =
Control.currentValue state.copy
attributes =
List.filterMap identity
[ settings.theme
, settings.position
, settings.width
, settings.padding
]
Control.currentValue state.attributes
in
[ Control.view SetDebugControlsState state |> fromUnstyled
[ Control.view SetCopy state.copy |> fromUnstyled
, Control.view SetAttributes state.attributes |> fromUnstyled
, Html.Styled.code [ css [ Css.display Css.block, Css.margin2 (Css.px 20) Css.zero ] ]
[ text <|
"Balloon.balloon [ "
++ String.join ", " (List.map Tuple.first attributes)
++ " ] "
++ "\""
++ settings.copy
++ copy
++ "\""
]
, Balloon.balloon (List.map Tuple.second attributes) (text settings.copy)
, Balloon.balloon (List.map Tuple.second attributes) (text copy)
]

View File

@ -6,6 +6,7 @@ module Examples.Button exposing (Msg, State, example)
-}
import Accessibility.Styled.Key as Key
import Category exposing (Category(..))
import Css exposing (middle, verticalAlign)
import Debug.Control as Control exposing (Control)
@ -28,6 +29,27 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ Button.link "Primary"
[ Button.small
, Button.fillContainerWidth
, Button.custom [ Key.tabbable False ]
]
, Button.link "Secondary"
[ Button.small
, Button.fillContainerWidth
, Button.secondary
, Button.css [ Css.marginTop (Css.px 8) ]
, Button.custom [ Key.tabbable False ]
]
, Button.link "Premium"
[ Button.small
, Button.fillContainerWidth
, Button.premium
, Button.css [ Css.marginTop (Css.px 8) ]
, Button.custom [ Key.tabbable False ]
]
]
, view = \state -> [ viewButtonExamples state ]
, categories = [ Buttons ]
, keyboardSupport = []

View File

@ -38,6 +38,7 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview = []
, view =
\state ->
[ viewInteractableCheckbox "styleguide-checkbox-interactable" state

View File

@ -6,6 +6,7 @@ module Examples.ClickableSvg exposing (Msg, State, example)
-}
import Accessibility.Styled.Key as Key
import Category exposing (Category(..))
import Css
import Debug.Control as Control exposing (Control)
@ -20,7 +21,6 @@ import Nri.Ui.ClickableSvg.V2 as ClickableSvg
import Nri.Ui.Colors.Extra exposing (fromCssColor, toCssColor)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Select.V7 as Select
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
import Nri.Ui.Tooltip.V2 as Tooltip
import Nri.Ui.UiIcon.V1 as UiIcon
@ -36,6 +36,23 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ ClickableSvg.link "ClickableSvg small"
UiIcon.link
[ ClickableSvg.small
, ClickableSvg.custom [ Key.tabbable False ]
]
, ClickableSvg.link "ClickableSvg medium"
UiIcon.link
[ ClickableSvg.medium
, ClickableSvg.custom [ Key.tabbable False ]
]
, ClickableSvg.link "ClickableSvg large"
UiIcon.link
[ ClickableSvg.large
, ClickableSvg.custom [ Key.tabbable False ]
]
]
, view =
\state ->
let

View File

@ -6,6 +6,7 @@ module Examples.ClickableText exposing (Msg, State, example)
-}
import Accessibility.Styled.Key as Key
import Category exposing (Category(..))
import Css exposing (middle, verticalAlign)
import Debug.Control as Control exposing (Control)
@ -15,7 +16,7 @@ import Html.Styled.Attributes exposing (css, id)
import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.ClickableText.V3 as ClickableText
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
import Nri.Ui.UiIcon.V1 as UiIcon
@ -32,6 +33,23 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ ClickableText.link "Small"
[ ClickableText.icon UiIcon.link
, ClickableText.small
, ClickableText.custom [ Key.tabbable False ]
]
, ClickableText.link "Medium"
[ ClickableText.icon UiIcon.link
, ClickableText.medium
, ClickableText.custom [ Key.tabbable False ]
]
, ClickableText.link "Large"
[ ClickableText.icon UiIcon.link
, ClickableText.large
, ClickableText.custom [ Key.tabbable False ]
]
]
, view = \state -> [ viewExamples state ]
, categories = [ Buttons ]
, keyboardSupport = []
@ -94,21 +112,23 @@ viewExamples (State control) =
[ Control.view (State >> SetState) control
|> fromUnstyled
, buttons model
, Text.smallBody []
[ text "Sometimes, we'll want our clickable links: "
, ClickableText.link model.label
[ ClickableText.small
, Maybe.map ClickableText.icon model.icon
|> Maybe.withDefault (ClickableText.custom [])
, Text.smallBody
[ Text.html
[ text "Sometimes, we'll want our clickable links: "
, ClickableText.link model.label
[ ClickableText.small
, Maybe.map ClickableText.icon model.icon
|> Maybe.withDefault (ClickableText.custom [])
]
, text " and clickable buttons: "
, ClickableText.button model.label
[ ClickableText.small
, ClickableText.onClick (ShowItWorked "ClickableText" "in-line button")
, Maybe.map ClickableText.icon model.icon
|> Maybe.withDefault (ClickableText.custom [])
]
, text " to show up in-line."
]
, text " and clickable buttons: "
, ClickableText.button model.label
[ ClickableText.small
, ClickableText.onClick (ShowItWorked "ClickableText" "in-line button")
, Maybe.map ClickableText.icon model.icon
|> Maybe.withDefault (ClickableText.custom [])
]
, text " to show up in-line."
]
]
|> div []

View File

@ -14,6 +14,7 @@ import Html.Styled.Attributes as Attributes exposing (css)
import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Colors.Extra
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Heading.V2 as Heading
import SolidColor exposing (highContrast)
@ -41,6 +42,12 @@ example =
, state = ()
, update = \_ state -> ( state, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview =
[ ( "green", Colors.green )
, ( "purple", Colors.purple )
, ( "mustard", Colors.mustard )
]
|> List.map viewPreviewSwatch
, view =
\_ ->
[ [ ( "gray20", Colors.gray20, "Main text" )
@ -117,6 +124,22 @@ example =
}
viewPreviewSwatch : ( String, Css.Color ) -> Html.Html msg
viewPreviewSwatch ( name, color ) =
Html.div
[ Attributes.css
[ Css.textAlign Css.center
, Css.padding2 (Css.px 8) Css.zero
, Css.margin2 (Css.px 4) Css.zero
, Css.borderRadius (Css.px 4)
, Css.backgroundColor color
, Css.color color
, Css.fontSize (Css.px 14)
]
]
[ Html.text name ]
viewColors : List ColorExample -> Html.Html msg
viewColors colors =
colors

View File

@ -33,6 +33,7 @@ example =
[ Browser.Events.onResize WindowResized
, Confetti.subscriptions ConfettiMsg state
]
, preview = []
, view =
\state ->
[ Button.button "Launch confetti!"

View File

@ -7,6 +7,7 @@ module Examples.Container exposing (Msg, State, example)
-}
import Category exposing (Category(..))
import CommonControls exposing (romeoAndJulietQuotation)
import Css
import Debug.Control as Control exposing (Control)
import Debug.Control.Extra as ControlExtra
@ -20,7 +21,7 @@ import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Container.V2 as Container
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
import Nri.Ui.UiIcon.V1 as UiIcon
@ -34,6 +35,17 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ Container.view []
, Container.view
[ Container.invalid
, Container.css [ Css.marginTop (Css.px 8) ]
]
, Container.view
[ Container.disabled
, Container.css [ Css.marginTop (Css.px 8) ]
]
]
, view =
\state ->
let
@ -149,26 +161,6 @@ controlContent =
]
romeoAndJulietQuotation : String
romeoAndJulietQuotation =
"""
Two households, both alike in dignity,
In fair Verona, where we lay our scene,
From ancient grudge break to new mutiny,
Where civil blood makes civil hands unclean.
From forth the fatal loins of these two foes
A pair of star-crossd lovers take their life;
Whose misadventured piteous overthrows
Do with their death bury their parents strife.
The fearful passage of their death-markd love,
And the continuance of their parents rage,
Which, but their childrens end, nought could remove,
Is now the two hours traffic of our stage;
The which if you with patient ears attend,
What here shall miss, our toil shall strive to mend.
"""
{-| -}
type Msg
= UpdateControl (Control (List (Container.Attribute Msg)))

View File

@ -16,7 +16,7 @@ import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Button.V10 as Button
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
{-| -}
@ -36,9 +36,15 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ DisclosureIndicator.medium [] False
, DisclosureIndicator.medium [] True
, DisclosureIndicator.large [] False
, DisclosureIndicator.large [] True
]
, view =
\state ->
[ Text.smallBodyGray [] [ Html.text "The disclosure indicator is only the caret. It is NOT a button -- you must create a button or clickabletext yourself!" ]
[ Text.smallBodyGray [ Text.plaintext "The disclosure indicator is only the caret. It is NOT a button -- you must create a button or clickabletext yourself!" ]
, Html.div [ css [ Css.displayFlex, Css.padding (Css.px 8) ] ]
[ Button.button "Toggle large indicator"
[ Button.onClick ToggleLarge, Button.small, Button.secondary ]

View File

@ -35,5 +35,6 @@ example =
, state = {}
, update = \_ state -> ( state, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview = [ Divider.view "Dividing Line" ]
, view = \state -> [ Divider.view "Dividing Line" ]
}

View File

@ -7,8 +7,9 @@ module Examples.Fonts exposing (example, State, Msg)
-}
import Category exposing (Category(..))
import Css exposing (Style)
import Example exposing (Example)
import Html.Styled as Html
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes exposing (css)
import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Fonts.V1 as Fonts
@ -35,6 +36,12 @@ example =
, state = ()
, update = \_ state -> ( state, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview =
[ ( "baseFont", Fonts.baseFont )
, ( "quizFont", Fonts.quizFont )
, ( "ugFont", Fonts.ugFont )
]
|> List.map viewPreview
, view =
\_ ->
[ Heading.h3 [] [ Html.text "baseFont" ]
@ -48,3 +55,20 @@ example =
[ Html.text "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" ]
]
}
viewPreview : ( String, Style ) -> Html msg
viewPreview ( name, font ) =
Html.div
[ css
[ Css.displayFlex
, Css.justifyContent Css.spaceBetween
, font
, Css.fontSize (Css.px 14)
]
]
[ Html.p [ css [ Css.margin2 (Css.px 8) Css.zero ] ]
[ Html.text name ]
, Html.p [ css [ Css.margin2 (Css.px 8) Css.zero ] ]
[ Html.text "AaBbCc" ]
]

View File

@ -35,6 +35,12 @@ example =
, state = ()
, update = \_ state -> ( state, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview =
[ Heading.h1 [] [ Html.text "h1" ]
, Heading.h2 [] [ Html.text "h2" ]
, Heading.h3 [] [ Html.text "h3" ]
, Heading.h4 [] [ Html.text "h4" ]
]
, view =
\_ ->
[ Heading.h1 [] [ Html.text "This is the main page heading." ]

View File

@ -1,4 +1,4 @@
module Examples.IconExamples exposing (view, viewWithCustomStyles)
module Examples.IconExamples exposing (preview, view, viewWithCustomStyles)
import Css
import Html.Styled as Html exposing (Html)
@ -6,7 +6,24 @@ import Html.Styled.Attributes exposing (css, style, title)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
preview : List Svg.Svg -> List (Html msg)
preview icons =
[ Html.div
[ css
[ Css.displayFlex
, Css.flexWrap Css.wrap
, Css.property "gap" "10px"
, Css.color Colors.gray45
]
]
(List.map
(Svg.withWidth (Css.px 30) >> Svg.withHeight (Css.px 30) >> Svg.toHtml)
icons
)
]
view : String -> List ( String, Svg.Svg ) -> Html msg
@ -55,5 +72,5 @@ viewIcon ( name, icon, style ) =
]
]
[ Html.div [ css style ] [ Svg.toHtml icon ]
, Text.smallBody [] [ Html.text name ]
, Text.smallBody [ Text.plaintext name ]
]

View File

@ -20,7 +20,7 @@ import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Loading.V1 as Loading
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
{-| -}
@ -94,6 +94,7 @@ example =
, state = init
, update = update
, subscriptions = subscriptions
, preview = []
, view =
\{ showLoadingFadeIn, showLoading, showSpinners } ->
[ if showLoading then
@ -113,7 +114,7 @@ example =
[ Loading.spinningPencil
|> Svg.withColor Colors.azure
|> Svg.toHtml
, Text.caption [] [ Html.text "By default, the spinningPencil is white. Showing as blue for visibility." ]
, Text.caption [ Text.plaintext "By default, the spinningPencil is white. Showing as blue for visibility." ]
, Loading.spinningDots
|> Svg.toHtml
]

View File

@ -10,9 +10,12 @@ import Category exposing (Category(..))
import Css
import Example exposing (Example)
import Examples.IconExamples as IconExamples
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes exposing (css)
import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Logo.V1 as Logo
import Nri.Ui.Svg.V1 as Svg
{-| -}
@ -35,6 +38,14 @@ example =
, state = ()
, update = \_ state -> ( state, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview =
Html.div [ css [ Css.marginBottom (Css.px 8) ] ] [ Svg.toHtml Logo.noredink ]
:: IconExamples.preview
[ Logo.facebook
, Logo.twitter
, Logo.cleverC
, Logo.googleG
]
, view =
\_ ->
[ IconExamples.viewWithCustomStyles "NRI"

View File

@ -47,6 +47,7 @@ example =
}
, { keys = [ Esc ], result = "Closes the menu" }
]
, preview = []
, view = view
}

View File

@ -5,6 +5,7 @@ import Category exposing (Category(..))
import CommonControls
import Css exposing (..)
import Debug.Control as Control exposing (Control)
import Debug.Control.Extra as ControlExtra
import Example exposing (Example)
import Html.Styled.Attributes as Attributes exposing (css, href)
import KeyboardSupport exposing (Direction(..), Key(..))
@ -14,6 +15,7 @@ import Nri.Ui.Message.V3 as Message
import Nri.Ui.Pennant.V2 as Pennant
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.UiIcon.V1 as UiIcon
import ViewHelpers exposing (viewExamples)
type alias State =
@ -26,26 +28,24 @@ init : State
init =
{ show = True
, control =
Control.record
(\a b c d e f -> List.filterMap identity [ a, b, c, d, e, f ])
|> Control.field "theme" controlTheme
|> Control.field "content" (Control.map Just controlContent)
|> Control.field "role" controlRole
|> Control.field "dismissable" controlDismissable
|> Control.field "css" controlCss
|> Control.field "icon" controlIcon
ControlExtra.list
|> ControlExtra.optionalListItem "theme" controlTheme
|> ControlExtra.listItem "content" controlContent
|> ControlExtra.optionalListItem "role" controlRole
|> ControlExtra.optionalListItem "dismissable" controlDismissable
|> ControlExtra.optionalListItem "css" controlCss
|> ControlExtra.optionalListItem "icon" controlIcon
}
controlTheme : Control (Maybe (Message.Attribute msg))
controlTheme : Control (Message.Attribute msg)
controlTheme =
Control.choice
[ ( "not set", Control.value Nothing )
, ( "tip", Control.value (Just Message.tip) )
, ( "error", Control.value (Just Message.error) )
, ( "alert", Control.value (Just Message.alert) )
, ( "success", Control.value (Just Message.success) )
, ( "customTheme", Control.map Just controlCustomTheme )
[ ( "tip", Control.value Message.tip )
, ( "error", Control.value Message.error )
, ( "alert", Control.value Message.alert )
, ( "success", Control.value Message.success )
, ( "customTheme", controlCustomTheme )
]
@ -64,13 +64,12 @@ controlCustomTheme =
)
controlIcon : Control (Maybe (Message.Attribute msg))
controlIcon : Control (Message.Attribute msg)
controlIcon =
Control.choice
[ ( "not set", Control.value Nothing )
, ( "premiumFlag", Control.value (Just (Message.icon Pennant.premiumFlag)) )
, ( "lock", Control.value (Just (Message.icon UiIcon.lock)) )
, ( "clock", Control.value (Just (Message.icon UiIcon.clock)) )
[ ( "premiumFlag", Control.value (Message.icon Pennant.premiumFlag) )
, ( "lock", Control.value (Message.icon UiIcon.lock) )
, ( "clock", Control.value (Message.icon UiIcon.clock) )
]
@ -123,37 +122,32 @@ controlContent =
]
controlRole : Control (Maybe (Message.Attribute msg))
controlRole : Control (Message.Attribute msg)
controlRole =
Control.choice
[ ( "not set", Control.value Nothing )
, ( "alertRole", Control.value (Just Message.alertRole) )
, ( "alertDialogRole", Control.value (Just Message.alertDialogRole) )
[ ( "alertRole", Control.value Message.alertRole )
, ( "alertDialogRole", Control.value Message.alertDialogRole )
]
controlDismissable : Control (Maybe (Message.Attribute Msg))
controlDismissable : Control (Message.Attribute Msg)
controlDismissable =
Control.maybe False <|
Control.value (Message.onDismiss Dismiss)
Control.value (Message.onDismiss Dismiss)
controlCss : Control (Maybe (Message.Attribute Msg))
controlCss : Control (Message.Attribute Msg)
controlCss =
Control.choice
[ ( "not set", Control.value Nothing )
, ( "css [ border3 (px 1) dashed red ]"
[ ( "css [ border3 (px 1) dashed red ]"
, Control.value
(Just (Message.css [ Css.border3 (Css.px 1) Css.dashed Colors.red ]))
(Message.css [ Css.border3 (Css.px 1) Css.dashed Colors.red ])
)
, ( "css [ border3 (px 2) solid purple, borderRadius4 (px 8) (px 8) zero zero ]"
, Control.value
(Just
(Message.css
[ Css.border3 (Css.px 2) Css.solid Colors.purple
, Css.borderRadius4 (Css.px 8) (Css.px 8) Css.zero Css.zero
]
)
(Message.css
[ Css.border3 (Css.px 2) Css.solid Colors.purple
, Css.borderRadius4 (Css.px 8) (Css.px 8) Css.zero Css.zero
]
)
)
]
@ -183,6 +177,11 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ Message.view [ Message.plaintext "Tiny tip" ]
, Message.view [ Message.success, Message.plaintext "Tiny success" ]
, Message.view [ Message.error, Message.plaintext "Tiny error" ]
]
, view =
\state ->
let
@ -201,21 +200,10 @@ example =
, Control.view UpdateControl state.control
|> Html.fromUnstyled
, orDismiss <|
Html.table [ css [ width (pct 100) ] ]
[ Html.tbody []
[ tr []
[ th [] [ Html.text "tiny" ]
, td [] [ Message.view (Message.tiny :: attributes) ]
]
, tr []
[ th [] [ Html.text "large" ]
, td [] [ Message.view (Message.large :: attributes) ]
]
, tr []
[ th [] [ Html.text "banner" ]
, td [] [ Message.view (Message.banner :: attributes) ]
]
]
viewExamples
[ ( "tiny", Message.view (Message.tiny :: attributes) )
, ( "large", Message.view (Message.large :: attributes) )
, ( "banner", Message.view (Message.banner :: attributes) )
]
, Heading.h3
[ Heading.css

View File

@ -7,21 +7,26 @@ module Examples.Modal exposing (Msg, State, example)
-}
import Accessibility.Styled as Html exposing (Html, div, h3, h4, p, span, text)
import Accessibility.Styled.Key as Key
import Browser.Dom as Dom
import Category exposing (Category(..))
import Css exposing (..)
import Debug.Control as Control exposing (Control)
import Debug.Control.Extra as ControlExtra
import Example exposing (Example)
import Html as Root
import Html.Styled.Attributes as Attributes exposing (css)
import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Button.V10 as Button
import Nri.Ui.Checkbox.V5 as Checkbox
import Nri.Ui.ClickableSvg.V2 as ClickableSvg
import Nri.Ui.ClickableText.V3 as ClickableText
import Nri.Ui.Colors.Extra
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.FocusTrap.V1 as FocusTrap
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Modal.V11 as Modal
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
import Nri.Ui.UiIcon.V1 as UiIcon
import Task
@ -44,10 +49,10 @@ init =
controlAttributes : Control (List Modal.Attribute)
controlAttributes =
Control.record (\a b c -> a :: b :: c :: [])
|> Control.field "Theme" controlTheme
|> Control.field "Title visibility" controlTitleVisibility
|> Control.field "Custom css" controlCss
ControlExtra.list
|> ControlExtra.listItem "Theme" controlTheme
|> ControlExtra.listItem "Title visibility" controlTitleVisibility
|> ControlExtra.listItem "Custom css" controlCss
type alias ViewSettings =
@ -124,6 +129,51 @@ example =
, state = init
, update = update
, subscriptions = subscriptions
, preview =
[ -- faking a mini version of the Modal component to give styleguide users a sense of what the
-- component might look like
div
[ css
[ Css.backgroundColor (Nri.Ui.Colors.Extra.withAlpha 0.9 Colors.navy)
, Css.borderRadius (Css.px 4)
, Css.padding2 (Css.px 25) Css.zero
, Css.displayFlex
, Css.alignItems Css.center
, Css.justifyContent Css.center
]
]
[ div
[ css
[ Css.backgroundColor Colors.white
, Css.padding (Css.px 10)
, Css.borderRadius (Css.px 10)
, Css.boxShadow5 Css.zero (Css.px 1) (Css.px 10) Css.zero (Css.rgba 0 0 0 0.35)
, Css.textAlign Css.center
, Css.color Colors.navy
, Fonts.baseFont
, Css.margin Css.auto
, Css.width (Css.px 100)
, Css.height (Css.px 60)
, Css.fontSize (Css.px 10)
, Css.fontWeight Css.bold
, Css.position Css.relative
]
]
[ text "Modal"
, ClickableSvg.link "Close"
UiIcon.x
[ ClickableSvg.exactWidth 10
, ClickableSvg.exactHeight 10
, ClickableSvg.css
[ Css.position absolute
, Css.top (Css.px 10)
, Css.right (Css.px 10)
]
, ClickableSvg.custom [ Key.tabbable False ]
]
]
]
]
, view =
\state ->
let
@ -228,7 +278,10 @@ launchModalButton settings =
viewModalContent : String -> Html msg
viewModalContent content =
Text.mediumBody [] [ span [ css [ whiteSpace preLine ] ] [ text content ] ]
Text.mediumBody
[ Text.css [ whiteSpace preLine ]
, Text.plaintext content
]
continueButtonId : String
@ -239,10 +292,9 @@ continueButtonId =
continueButton : Html Msg
continueButton =
Button.button "Continue"
[ Button.primary
, Button.onClick CloseModal
, Button.custom [ Attributes.id continueButtonId ]
, Button.large
[ Button.onClick CloseModal
, Button.id continueButtonId
, Button.modal
]
@ -255,9 +307,8 @@ closeClickableText : Html Msg
closeClickableText =
ClickableText.button "Close"
[ ClickableText.onClick CloseModal
, ClickableText.large
, ClickableText.custom [ Attributes.id closeClickableTextId ]
, ClickableText.css [ Css.marginTop (Css.px 15) ]
, ClickableText.modal
, ClickableText.id closeClickableTextId
]

View File

@ -17,6 +17,7 @@ import Html.Styled.Attributes exposing (css)
import Http
import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Page.V3 as Page exposing (RecoveryText(..))
@ -62,6 +63,32 @@ example =
, state = { httpError = CommonControls.httpError, recoveryText = initRecoveryText }
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ -- faking a mini version of the Page component to give styleguide users a sense of what the
-- component might look like
Html.div
[ css
[ Css.displayFlex
, Css.alignItems Css.center
, Css.flexDirection Css.column
, Css.backgroundColor Colors.white
, Css.borderRadius (Css.px 4)
, Css.padding (Css.px 20)
]
]
[ Html.div [ css [ Css.fontSize (Css.px 40) ] ] [ Html.text "😵" ]
, Html.p
[ css
[ Css.color Colors.navy
, Fonts.baseFont
, Css.fontWeight Css.bold
, Css.textAlign Css.center
, Css.margin Css.zero
]
]
[ Html.text "There was a problem!" ]
]
]
, view =
\model ->
let

View File

@ -38,6 +38,12 @@ example =
, state = ()
, update = \_ state -> ( state, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview =
IconExamples.preview
[ Pennant.premiumFlag
, Pennant.expiredPremiumFlag
, Pennant.disabledPremiumFlag
]
, view =
\_ ->
[ IconExamples.viewWithCustomStyles "Premium Pennants"

View File

@ -23,7 +23,7 @@ import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel)
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Modal.V10 as Modal
import Nri.Ui.RadioButton.V2 as RadioButton
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
{-| -}
@ -34,6 +34,7 @@ example =
, state = init
, update = update
, subscriptions = subscriptions
, preview = []
, view = view
, categories = [ Inputs ]
, keyboardSupport =
@ -65,7 +66,7 @@ view model =
Modal.MultipleFocusableElements
(\{ firstFocusableElement, autofocusElement, lastFocusableElement, closeButton } ->
{ content =
[ Text.mediumBody [] [ text "Often, we'll launch a modal showing the benefits of premium when a locked radio button is clicked." ]
[ Text.mediumBody [ Text.plaintext "Often, we'll launch a modal showing the benefits of premium when a locked radio button is clicked." ]
, closeButton (autofocusElement :: firstFocusableElement)
]
, footer =

View File

@ -36,6 +36,7 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview = []
, view =
\state ->
let

View File

@ -6,69 +6,71 @@ module Examples.Select exposing (Msg, State, example)
-}
import Accessibility.Styled.Key as Key
import Category exposing (Category(..))
import Css
import Debug.Control as Control exposing (Control)
import Debug.Control.Extra as ControlExtra
import Example exposing (Example)
import Html.Styled
import Html.Styled.Attributes
import Html.Styled.Attributes exposing (css)
import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Select.V7 as Select
import Nri.Ui.Select.V8 as Select exposing (Choice)
{-| -}
example : Example State Msg
example =
{ name = "Select"
, version = 7
, version = 8
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, categories = [ Inputs ]
, keyboardSupport = []
, preview =
[ Select.view "Label" [ Select.custom [ Key.tabbable False ] ]
, Select.view "Hidden label"
[ Select.hiddenLabel
, Select.defaultDisplayText "Hidden label"
, Select.custom [ Key.tabbable False ]
]
]
, view =
\state ->
[ Html.Styled.label
[ Html.Styled.Attributes.for "tortilla-selector" ]
[ Heading.h3 [] [ Html.Styled.text "Tortilla Selector" ] ]
, Select.view
{ current = Nothing
, choices =
[ { label = "Tacos", value = "Tacos" }
, { label = "Burritos", value = "Burritos" }
, { label = "Enchiladas", value = "Enchiladas" }
]
, id = "tortilla-selector"
, valueToString = identity
, defaultDisplayText = Just "Select a tasty tortilla based treat!"
, isInError = False
}
|> Html.Styled.map ConsoleLog
, Html.Styled.label
[ Html.Styled.Attributes.for "errored-selector" ]
[ Heading.h3 [] [ Html.Styled.text "Errored Selector" ] ]
, Select.view
{ current = Nothing
, choices = []
, id = "errored-selector"
, valueToString = identity
, defaultDisplayText = Just "Please select an option"
, isInError = True
}
|> Html.Styled.map ConsoleLog
, Html.Styled.label
[ Html.Styled.Attributes.for "overflowed-selector" ]
[ Heading.h3 [] [ Html.Styled.text "Selector with Overflowed Text" ] ]
let
label =
Control.currentValue state.label
( attributesCode, attributes ) =
List.unzip (Control.currentValue state.attributes)
in
[ Control.view UpdateLabel state.label
|> Html.Styled.fromUnstyled
, Control.view UpdateAttributes state.attributes
|> Html.Styled.fromUnstyled
, Html.Styled.div
[ Html.Styled.Attributes.css [ Css.maxWidth (Css.px 400) ] ]
[ Select.view
{ current = Nothing
, choices = []
, id = "overflowed-selector"
, valueToString = identity
, defaultDisplayText = Just "Look at me, I design coastlines, I got an award for Norway. Where's the sense in that?"
, isInError = False
}
[ css [ Css.displayFlex, Css.alignItems Css.flexStart ]
]
[ Html.Styled.code
[ css
[ Css.display Css.block
, Css.margin2 (Css.px 20) Css.zero
, Css.whiteSpace Css.preWrap
, Css.maxWidth (Css.px 500)
]
]
[ Html.Styled.text <|
"Select.view \""
++ label
++ "\""
++ "\n [ "
++ String.join "\n , " attributesCode
++ "\n ] "
]
, Select.view label attributes
|> Html.Styled.map ConsoleLog
]
]
@ -76,19 +78,125 @@ example =
{-| -}
init : State
init =
Nothing
type alias State =
{ label : Control String
, attributes : Control Settings
}
{-| -}
type alias State =
Maybe String
init : State
init =
{ label = Control.string "Tortilla Selector"
, attributes = initControls
}
type alias Settings =
List ( String, Select.Attribute String )
initControls : Control Settings
initControls =
ControlExtra.list
|> ControlExtra.listItem "choices"
(Control.map
(\( code, choices ) ->
( "Select.choices identity" ++ code
, Select.choices identity choices
)
)
initChoices
)
|> ControlExtra.optionalListItem "hiddenLabel"
(Control.value ( "Select.hiddenLabel", Select.hiddenLabel ))
|> ControlExtra.optionalListItem "defaultDisplayText"
(Control.map
(\str ->
( "Select.defaultDisplayText \"" ++ str ++ "\""
, Select.defaultDisplayText str
)
)
(Control.string "Select a tasty tortilla based treat!")
)
|> ControlExtra.optionalListItem "containerCss"
(Control.choice
[ ( "flex-basis: 300px"
, Control.value
( "Select.containerCss [ Css.flexBasis (Css.px 300) ]"
, Select.containerCss [ Css.flexBasis (Css.px 300) ]
)
)
, ( "background-color: lichen"
, Control.value
( "Select.containerCss [ Css.backgroundColor Colors.lichen ]"
, Select.containerCss [ Css.backgroundColor Colors.lichen ]
)
)
]
)
|> ControlExtra.optionalListItem "noMargin"
(Control.map
(\bool ->
( "Select.noMargin " ++ Debug.toString bool
, Select.noMargin bool
)
)
(Control.bool True)
)
|> ControlExtra.optionalListItem "errorIf"
(Control.map
(\bool ->
( "Select.errorIf " ++ Debug.toString bool
, Select.errorIf bool
)
)
(Control.bool True)
)
|> ControlExtra.optionalListItem "errorMessage"
(Control.map
(\str ->
( "Select.errorMessage (Just \"" ++ str ++ "\")"
, Select.errorMessage (Just str)
)
)
(Control.string "The right item must be selected.")
)
initChoices : Control ( String, List (Choice String) )
initChoices =
Control.choice
[ ( "Short choices"
, ( """
[ { label = "Tacos", value = "tacos" }
, { label = "Burritos", value = "burritos" }
, { label = "Enchiladas", value = "enchiladas" }
]"""
, [ { label = "Tacos", value = "tacos" }
, { label = "Burritos", value = "burritos" }
, { label = "Enchiladas", value = "enchiladas" }
]
)
|> Control.value
)
, ( "Overflowing text choices"
, ( """
[ { label = "Look at me, I design coastlines, I got an award for Norway. Where's the sense in that? My mistress' eyes are nothing like the sun. Coral be far more red than her lips red.", value = "design-coastlines" }
]"""
, [ { label = "Look at me, I design coastlines, I got an award for Norway. Where's the sense in that? My mistress' eyes are nothing like the sun. Coral be far more red than her lips red.", value = "design-coastlines" }
]
)
|> Control.value
)
]
{-| -}
type Msg
= ConsoleLog String
| UpdateLabel (Control String)
| UpdateAttributes (Control Settings)
{-| -}
@ -100,4 +208,14 @@ update msg state =
_ =
Debug.log "SelectExample" message
in
( Just message, Cmd.none )
( state, Cmd.none )
UpdateLabel label ->
( { state | label = label }
, Cmd.none
)
UpdateAttributes attributes ->
( { state | attributes = attributes }
, Cmd.none
)

View File

@ -42,6 +42,7 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview = []
, view =
\state ->
[ Keyed.node "div"

View File

@ -40,6 +40,7 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview = []
, view =
\state ->
[ viewModal state.modal

View File

@ -41,6 +41,7 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview = []
, view =
\{ sortState } ->
let

View File

@ -17,7 +17,6 @@ import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Colors.Extra exposing (fromCssColor, toCssColor)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Select.V7 as Select
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.UiIcon.V1 as UiIcon
import SolidColor exposing (SolidColor)
@ -33,6 +32,7 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview = []
, view =
\state ->
[ viewSettings state

View File

@ -6,12 +6,12 @@ module Examples.Switch exposing (Msg, State, example)
-}
import Accessibility.Styled.Key as Key
import Category
import Example exposing (Example)
import Html.Styled as Html
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Switch.V1 as Switch
import Nri.Ui.Text.V5 as Text
{-| -}
@ -31,40 +31,47 @@ example =
, state = True
, update = \(Switch new) _ -> ( new, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview =
[ Switch.view
[ Switch.label (Html.text "Toggle On")
, Switch.custom [ Key.tabbable False ]
]
False
, Switch.view
[ Switch.label (Html.text "Toggle Off")
, Switch.custom [ Key.tabbable False ]
]
True
]
, view =
\interactiveIsOn ->
[ Heading.h3 [] [ Html.text "Interactive" ]
, Text.mediumBody []
[ Switch.view
[ Switch.onSwitch Switch
, Switch.id "switch-interactive"
, Switch.label
(if interactiveIsOn then
Html.text "On"
, Switch.view
[ Switch.onSwitch Switch
, Switch.id "switch-interactive"
, Switch.label
(if interactiveIsOn then
Html.text "On"
else
Html.text "Off"
)
]
interactiveIsOn
else
Html.text "Off"
)
]
, Heading.h3 [] [ Html.text "Disabled" ]
, Text.mediumBody []
[ Switch.view
[ Switch.disabled
, Switch.id "switch-disabled-on"
, Switch.label (Html.text "Permanently on")
]
True
interactiveIsOn
, Heading.h3 [] [ Html.text "Disabled (On)" ]
, Switch.view
[ Switch.disabled
, Switch.id "switch-disabled-on"
, Switch.label (Html.text "Permanently on")
]
, Text.mediumBody []
[ Switch.view
[ Switch.disabled
, Switch.id "switch-disabled-off"
, Switch.label (Html.text "Permanently off")
]
False
True
, Heading.h3 [] [ Html.text "Disabled (Off)" ]
, Switch.view
[ Switch.disabled
, Switch.id "switch-disabled-off"
, Switch.label (Html.text "Permanently off")
]
False
]
, categories = [ Category.Inputs ]
, keyboardSupport = [{- TODO -}]

View File

@ -37,6 +37,29 @@ example =
, subscriptions = \_ -> Sub.none
, categories = [ Tables, Layout ]
, keyboardSupport = []
, preview =
[ Table.view
[ Table.string
{ header = "A"
, value = .a
, width = px 50
, cellStyles = always []
}
, Table.string
{ header = "B"
, value = .b
, width = px 50
, cellStyles = always []
}
]
[ { a = "Row 1 A"
, b = "Row 1 B"
}
, { a = "Row 2 A"
, b = "Row 2 B"
}
]
]
, view =
\() ->
let

View File

@ -19,10 +19,13 @@ import Html.Styled as Html exposing (Html, fromUnstyled)
import Html.Styled.Attributes exposing (css)
import KeyboardSupport exposing (Key(..))
import List.Zipper exposing (Zipper)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Tabs.V7 as Tabs exposing (Alignment(..), Tab)
import Nri.Ui.Text.V6 as Text
import Nri.Ui.Tooltip.V2 as Tooltip
import Nri.Ui.UiIcon.V1 as UiIcon
import Routes
import Task
@ -117,9 +120,14 @@ update msg model =
)
exampleName : String
exampleName =
"Tabs"
example : Example State Msg
example =
{ name = "Tabs"
{ name = exampleName
, version = 7
, categories = [ Layout ]
, keyboardSupport =
@ -136,6 +144,49 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ -- faking a mini version of the Tabs component to give styleguide users a sense of what the
-- component might look like
Html.div [ css [ Css.displayFlex, Css.flexWrap Css.wrap ] ]
[ Html.div
[ css
[ Css.backgroundColor Colors.white
, Css.padding (Css.px 4)
, Css.borderRadius4 (Css.px 4) (Css.px 4) Css.zero Css.zero
, Css.border3 (Css.px 1) Css.solid Colors.navy
, Css.borderBottomWidth Css.zero
]
]
[ Text.smallBody [ Text.plaintext "Tab 1" ] ]
, Html.div
[ css [ Css.width (Css.px 4), Css.borderBottom3 (Css.px 1) Css.solid Colors.navy ]
]
[]
, Html.div
[ css
[ Css.backgroundColor Colors.frost
, Css.padding (Css.px 4)
, Css.borderRadius4 (Css.px 4) (Css.px 4) Css.zero Css.zero
, Css.border3 (Css.px 1) Css.solid Colors.navy
]
]
[ Text.smallBody [ Text.plaintext "Tab 1" ] ]
, Html.div
[ css
[ Css.width (Css.px 30)
, Css.borderBottom3 (Css.px 1) Css.solid Colors.navy
]
]
[]
, Html.div
[ css
[ Css.paddingTop (Css.px 4)
, Css.minWidth (Css.px 100)
]
]
[ Text.caption [ Text.plaintext "Tab 1 content" ] ]
]
]
, view =
\model ->
let
@ -167,7 +218,7 @@ allTabs openTooltipId labelledBy =
|> Svg.toHtml
in
[ Tabs.build { id = First, idString = "tab-0" }
([ Tabs.spaHref "/#/doodad/Tabs"
([ Tabs.spaHref <| Routes.toString (Routes.Doodad exampleName)
, Tabs.tabString "1"
, Tabs.withTooltip
[ Tooltip.plaintext "Link Example"

View File

@ -7,79 +7,137 @@ module Examples.Text exposing (example, State, Msg)
-}
import Category exposing (Category(..))
import CommonControls exposing (exampleHtml, quickBrownFox, romeoAndJulietQuotation)
import Css
import Debug.Control as Control exposing (Control)
import Debug.Control.Extra as ControlExtra
import Example exposing (Example)
import Html.Styled as Html
import Html.Styled.Attributes as Attributes
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes exposing (css)
import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Text.V5 as Text
{-| -}
type alias State =
()
{-| -}
type alias Msg =
()
import Nri.Ui.Text.V6 as Text
{-| -}
example : Example State Msg
example =
{ name = "Text"
, version = 5
, version = 6
, categories = [ Text ]
, keyboardSupport = []
, state = ()
, update = \_ state -> ( state, Cmd.none )
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ ( "caption", Text.caption )
, ( "smallBody", Text.smallBody )
, ( "mediumBody", Text.mediumBody )
, ( "ugMediumBody", Text.ugMediumBody )
]
|> List.map viewPreview
, view =
\_ ->
\state ->
let
exampleHtml kind =
[ Html.text "This is a "
, Html.strong [] [ Html.text kind ]
, Html.text ". "
, Html.a
[ Attributes.href "http://www.noredink.com"
, Attributes.target "_blank"
]
[ Html.text "The quick brown fox jumps over the lazy dog." ]
, Html.text " Be on the lookout for a new and improved assignment creation form! Soon, you'll be able to easily see a summary of the content you're assigning, as well as an estimate for how long the assignment will take."
]
exampleUGHtml kind =
[ Html.text "This is a "
, Html.strong [] [ Html.text kind ]
, Html.text ". The quick brown fox jumps over the lazy dog."
, Html.text " When I stepped out, into the bright sunlight from the darkness of the movie house, I had only two things on my mind: Paul Newman, and a ride home."
]
attributes =
Control.currentValue state.control
in
[ Text.caption [] [ Html.text "NOTE: When using these styles, please read the documentation in the Elm module about \"Understanding spacing\"" ]
[ Text.caption [ Text.plaintext "NOTE: When using these styles, please read the documentation in the Elm module about \"Understanding spacing\"" ]
, Control.view UpdateControl state.control
|> Html.fromUnstyled
, Heading.h2 [ Heading.style Heading.Top ] [ Html.text "Paragraph styles" ]
, Text.mediumBody [] (exampleHtml "mediumBody")
, Text.smallBody [] (exampleHtml "smallBody")
, Text.smallBodyGray [] (exampleHtml "smallBodyGray")
, Text.caption [] (exampleHtml "caption")
, viewExamples
[ ( "mediumBody", Text.mediumBody )
, ( "smallBody", Text.smallBody )
, ( "smallBodyGray", Text.smallBodyGray )
, ( "caption", Text.caption )
]
attributes
, Heading.h2 [ Heading.style Heading.Top ] [ Html.text "Paragraph styles for user-authored content" ]
, Text.ugMediumBody [] (exampleUGHtml "ugMediumBody")
, Text.ugSmallBody [] (exampleUGHtml "ugSmallBody")
, Heading.h2 [ Heading.style Heading.Top ] [ Html.text "One-Off Styles" ]
, Text.mediumBody
[ Text.css [ Css.padding (Css.px 20) ] ]
[ Html.text "I've got more padding than my siblings!" ]
, Html.div
[ Attributes.css
[ Css.width (Css.px 80)
, Css.border3 (Css.px 1) Css.solid (Css.hex "000")
]
]
[ Text.mediumBody
[ Text.noBreak ]
[ Html.text "I won't ever break, no matter how narrow my container is." ]
, viewExamples
[ ( "ugMediumBody", Text.ugMediumBody )
, ( "ugSmallBody", Text.ugSmallBody )
]
attributes
]
}
viewPreview : ( String, List (Text.Attribute msg) -> Html msg ) -> Html msg
viewPreview ( name, view ) =
view [ Text.plaintext name ]
viewExamples : List ( String, List (Text.Attribute msg) -> Html msg ) -> List (Text.Attribute msg) -> Html msg
viewExamples examples attributes =
let
viewExample ( name, view ) =
Html.tr []
[ Html.th [] [ Html.text name ]
, Html.td [] [ view attributes ]
]
in
Html.table [ css [ Css.width (Css.pct 100) ] ]
[ Html.tbody [] <|
List.map viewExample examples
]
{-| -}
type alias State =
{ control : Control (List (Text.Attribute Msg))
}
{-| -}
init : State
init =
{ control =
ControlExtra.list
|> ControlExtra.listItem "content" controlContent
|> ControlExtra.listItem "noBreak"
(Control.map Text.noBreak (Control.bool False))
|> ControlExtra.optionalListItem "css"
(Control.value
(Text.css
[ Css.border3 (Css.px 1) Css.solid Colors.aqua
, Css.color Colors.aquaDark
]
)
)
}
controlContent : Control (Text.Attribute msg)
controlContent =
Control.choice
[ ( "HTML"
, Control.value (Text.html exampleHtml)
)
, ( "plain text (short)"
, Control.string quickBrownFox
|> Control.map Text.plaintext
)
, ( "plain text (long)"
, Control.stringTextarea romeoAndJulietQuotation
|> Control.map Text.plaintext
)
, ( "markdown"
, Control.string romeoAndJulietQuotation
|> Control.map Text.markdown
)
]
{-| -}
type Msg
= UpdateControl (Control (List (Text.Attribute Msg)))
{-| -}
update : Msg -> State -> ( State, Cmd Msg )
update msg state =
case msg of
UpdateControl newControl ->
( { state | control = newControl }, Cmd.none )

View File

@ -32,6 +32,7 @@ example =
, state = ()
, update = \_ state -> ( state, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview = [ TextWriting.footnote [ text "This is a footnote. " ] ]
, view =
\_ ->
let

View File

@ -44,6 +44,7 @@ example =
, subscriptions = \_ -> Sub.none
, categories = [ Inputs ]
, keyboardSupport = []
, preview = []
, view =
\state ->
[ Heading.h1 [] [ Html.text "Textarea controls" ]

View File

@ -7,9 +7,11 @@ module Examples.TextInput exposing (Msg, State, example)
-}
import Accessibility.Styled as Html exposing (..)
import Accessibility.Styled.Key as Key
import Category exposing (Category(..))
import Css exposing (..)
import Debug.Control as Control exposing (Control)
import Debug.Control.Extra as ControlExtra
import Dict exposing (Dict)
import Example exposing (Example)
import Html.Styled.Attributes exposing (css)
@ -17,259 +19,212 @@ import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Message.V3 as Message
import Nri.Ui.TextInput.V6 as TextInput
{-| -}
type Msg
= SetTextInput Id String
| SetNumberInput (Maybe Int)
| SetFloatInput (Maybe Float)
| SetPassword String
| SetSearchTerm String
| UpdateControl (Control ExampleConfig)
| HitEnter
{-| -}
type alias State =
{ numberInputValue : Maybe Int
, floatInputValue : Maybe Float
, stringInputValues : Dict Id String
, passwordInputValue : String
, searchInputValue : String
, control : Control ExampleConfig
, enterCount : Int
}
type alias ExampleConfig =
{ label : String
, maybePlaceholderAttribute : Maybe (TextInput.Attribute Msg)
, maybeErrorAttribute1 : Maybe (TextInput.Attribute Msg)
, maybeErrorAttribute2 : Maybe (TextInput.Attribute Msg)
, maybeShowLabelAttribute : Maybe (TextInput.Attribute Msg)
, maybeDisabledAttribute : Maybe (TextInput.Attribute Msg)
, maybeLoadingAttribute : Maybe (TextInput.Attribute Msg)
, noMarginAttribute : TextInput.Attribute Msg
, onBlur : Bool
, onReset : Bool
}
import Nri.Ui.TextInput.V7 as TextInput
import ViewHelpers exposing (viewExamples)
{-| -}
example : Example State Msg
example =
{ name = "TextInput"
, version = 6
, version = 7
, categories = [ Inputs ]
, keyboardSupport = []
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ TextInput.view "Text Input"
[ TextInput.custom [ Key.tabbable False ]
]
, TextInput.view "Errored"
[ TextInput.value "invalid content"
, TextInput.errorIf True
, TextInput.custom [ Key.tabbable False ]
]
]
, view =
\state ->
let
exampleConfig =
Control.currentValue state.control
attributes { setField, onBlur, onReset, onEnter } =
List.filterMap identity
[ exampleConfig.maybeErrorAttribute1
, exampleConfig.maybeErrorAttribute2
, exampleConfig.maybePlaceholderAttribute
, exampleConfig.maybeShowLabelAttribute
, exampleConfig.maybeDisabledAttribute
, exampleConfig.maybeLoadingAttribute
, Just exampleConfig.noMarginAttribute
, if exampleConfig.onBlur then
Just (TextInput.onBlur (setField onBlur))
toExample { name, toString, inputType, onFocus, onBlur, onEnter } index =
( name
, TextInput.view exampleConfig.label
(exampleConfig.attributes
++ [ TextInput.id ("text-input__" ++ name ++ "-example")
, inputType (toString >> SetInput index)
|> TextInput.map toString identity
, TextInput.value (Maybe.withDefault "" (Dict.get index state.inputValues))
]
++ List.filterMap identity
[ if exampleConfig.onFocus then
Just (TextInput.onFocus (SetInput index onFocus))
else
Nothing
, if exampleConfig.onReset then
Just (TextInput.onReset (setField onReset))
else
Nothing
, if exampleConfig.onBlur then
Just (TextInput.onBlur (SetInput index onBlur))
else
Nothing
, Just <| TextInput.onEnter onEnter
]
else
Nothing
, if exampleConfig.onEnter then
Just (TextInput.onEnter (SetInput index onEnter))
else
Nothing
]
)
)
in
[ Control.view UpdateControl state.control
|> Html.fromUnstyled
, Html.div
[ css
[ property "display" "grid"
, property "grid-template-columns" "auto 1fr"
, property "grid-gap" "10px"
]
]
[ Heading.h3 [] [ text "TextInput.text" ]
, TextInput.view (exampleConfig.label ++ " (text)")
(TextInput.text (SetTextInput 1))
(attributes
{ setField = SetTextInput 1
, onBlur = "Blurred!!!"
, onReset = ""
, onEnter = HitEnter
}
)
(Maybe.withDefault "" <| Dict.get 1 state.stringInputValues)
, Heading.h3 [] [ text "TextInput.number" ]
, TextInput.view (exampleConfig.label ++ " (number)")
(TextInput.number SetNumberInput)
(TextInput.id "hey-this-is-a-test-id"
:: attributes
{ setField = SetNumberInput
, onBlur = Just 10000000
, onReset = Nothing
, onEnter = HitEnter
}
)
state.numberInputValue
, Heading.h3 [] [ text "TextInput.float" ]
, TextInput.view (exampleConfig.label ++ " (float)")
(TextInput.float SetFloatInput)
(attributes
{ setField = SetFloatInput
, onBlur = Just 1.00000001
, onReset = Nothing
, onEnter = HitEnter
}
)
state.floatInputValue
, Heading.h3 [] [ text "TextInput.password" ]
, TextInput.view (exampleConfig.label ++ " (password)")
(TextInput.password SetPassword)
(attributes
{ setField = SetPassword
, onBlur = "Blurred!!!"
, onReset = ""
, onEnter = HitEnter
}
)
state.passwordInputValue
, Heading.h3 [] [ text "TextInput.email" ]
, TextInput.view (exampleConfig.label ++ " (email)")
(TextInput.email (SetTextInput 2))
(attributes
{ setField = SetTextInput 2
, onBlur = "Blurred!!!"
, onReset = ""
, onEnter = HitEnter
}
)
(Maybe.withDefault "" <| Dict.get 2 state.stringInputValues)
, Heading.h3 [] [ Html.text "TextInput.writing" ]
, TextInput.view (exampleConfig.label ++ " (writing)")
(TextInput.text (SetTextInput 4))
(TextInput.writing
:: attributes
{ setField = SetTextInput 4
, onBlur = "Blurred!!!"
, onReset = ""
, onEnter = HitEnter
}
)
(Maybe.withDefault "" <| Dict.get 4 state.stringInputValues)
, Heading.h3 [] [ Html.text "TextInput.search" ]
, TextInput.view (exampleConfig.label ++ " (search)")
(TextInput.search SetSearchTerm)
(attributes
{ setField = SetSearchTerm
, onBlur = "Blurred!!!"
, onReset = ""
, onEnter = HitEnter
}
)
state.searchInputValue
, Heading.h3 [] [ text "TextInput.css" ]
, TextInput.view (exampleConfig.label ++ " (custom CSS)")
(TextInput.text (SetTextInput 8))
(TextInput.css [ Css.backgroundColor Colors.azure ]
:: attributes
{ setField = SetTextInput 8
, onBlur = "Blurred!!!"
, onReset = ""
, onEnter = HitEnter
}
)
(Maybe.withDefault "" <| Dict.get 8 state.stringInputValues)
, Message.view
[ Message.tiny
, Message.tip
, Message.plaintext <| "Hit enter " ++ String.fromInt state.enterCount ++ " times"
]
, (viewExamples << List.indexedMap (\i toView -> toView i))
[ toExample
{ name = "text"
, toString = identity
, inputType = TextInput.text
, onFocus = "Focused!!!"
, onBlur = "Blurred!!!"
, onEnter = "Entered!!!"
}
, toExample
{ name = "number"
, toString = Maybe.map String.fromInt >> Maybe.withDefault ""
, inputType = TextInput.number
, onFocus = "1234"
, onBlur = "10000000"
, onEnter = "20000000"
}
, toExample
{ name = "float"
, toString = Maybe.map String.fromFloat >> Maybe.withDefault ""
, inputType = TextInput.float
, onFocus = "123"
, onBlur = "1.00000001"
, onEnter = "100000001.1"
}
, toExample
{ name = "newPassword"
, toString = identity
, inputType =
\onInput ->
TextInput.newPassword
{ onInput = onInput
, showPassword = state.showPassword
, setShowPassword = SetShowPassword
}
, onFocus = "Focused!!!"
, onBlur = "Blurred!!!"
, onEnter = "Entered!!!"
}
, toExample
{ name = "email"
, toString = identity
, inputType = TextInput.email
, onFocus = "Focused!!!"
, onBlur = "Blurred!!!"
, onEnter = "Entered!!!"
}
, toExample
{ name = "search"
, toString = identity
, inputType = TextInput.search
, onFocus = "Focused!!!"
, onBlur = "Blurred!!!"
, onEnter = "Entered!!!"
}
]
]
}
{-| -}
type alias State =
{ inputValues : Dict Int String
, showPassword : Bool
, control : Control ExampleConfig
}
{-| -}
init : State
init =
{ numberInputValue = Nothing
, floatInputValue = Nothing
, stringInputValues = Dict.empty
, passwordInputValue = ""
, searchInputValue = ""
, control =
Control.record ExampleConfig
|> Control.field "label" (Control.string "Assignment name")
|> Control.field "TextInput.placeholder"
(Control.maybe True <|
Control.map TextInput.placeholder <|
Control.string "Learning with commas"
)
|> Control.field "TextInput.hiddenLabel"
(Control.maybe False (Control.value TextInput.hiddenLabel))
|> Control.field "TextInput.errorIf"
(Control.maybe False (Control.map TextInput.errorIf <| Control.bool True))
|> Control.field "TextInput.errorMessage"
(Control.maybe False (Control.map TextInput.errorMessage <| Control.maybe True <| Control.string "The statement must be true."))
|> Control.field "TextInput.disabled"
(Control.maybe False (Control.value TextInput.disabled))
|> Control.field "TextInput.loading"
(Control.maybe False (Control.value TextInput.loading))
|> Control.field "TextInput.noMargin"
(Control.map TextInput.noMargin (Control.bool False))
|> Control.field "TextInput.onBlur"
(Control.bool False)
|> Control.field "TextInput.onReset"
(Control.bool False)
, enterCount = 0
{ inputValues = Dict.empty
, showPassword = False
, control = initControl
}
type alias ExampleConfig =
{ label : String
, attributes : List (TextInput.Attribute String Msg)
, onFocus : Bool
, onBlur : Bool
, onEnter : Bool
}
initControl : Control ExampleConfig
initControl =
Control.record ExampleConfig
|> Control.field "label" (Control.string "Assignment name")
|> Control.field "attributes" controlAttributes
|> Control.field "onFocus" (Control.bool False)
|> Control.field "onBlur" (Control.bool False)
|> Control.field "onEnter" (Control.bool False)
controlAttributes : Control (List (TextInput.Attribute value msg))
controlAttributes =
ControlExtra.list
|> ControlExtra.optionalListItem "placeholder"
(Control.map TextInput.placeholder <|
Control.string "Learning with commas"
)
|> ControlExtra.optionalListItem "hiddenLabel"
(Control.value TextInput.hiddenLabel)
|> ControlExtra.optionalListItem "errorIf"
(Control.map TextInput.errorIf <| Control.bool True)
|> ControlExtra.optionalListItem "errorMessage"
(Control.map (Just >> TextInput.errorMessage) <| Control.string "The statement must be true.")
|> ControlExtra.optionalListItem "guidance"
(Control.map TextInput.guidance <| Control.string "The statement must be true.")
|> ControlExtra.optionalListItem "disabled"
(Control.value TextInput.disabled)
|> ControlExtra.optionalListItem "loading"
(Control.value TextInput.loading)
|> ControlExtra.optionalListItem "writing"
(Control.value TextInput.writing)
|> ControlExtra.listItem "noMargin"
(Control.map TextInput.noMargin (Control.bool False))
|> ControlExtra.optionalListItem "css"
(Control.value (TextInput.css [ Css.backgroundColor Colors.azure ]))
{-| -}
type Msg
= SetInput Int String
| SetShowPassword Bool
| UpdateControl (Control ExampleConfig)
{-| -}
update : Msg -> State -> ( State, Cmd Msg )
update msg state =
case msg of
SetTextInput id textInputValue ->
( { state | stringInputValues = Dict.insert id textInputValue state.stringInputValues }, Cmd.none )
SetInput id string ->
( { state | inputValues = Dict.insert id string state.inputValues }
, Cmd.none
)
SetNumberInput numberInputValue ->
( { state | numberInputValue = numberInputValue }, Cmd.none )
SetFloatInput floatInputValue ->
( { state | floatInputValue = floatInputValue }, Cmd.none )
SetPassword password ->
( { state | passwordInputValue = password }, Cmd.none )
SetSearchTerm searchInputValue ->
( { state | searchInputValue = searchInputValue }, Cmd.none )
SetShowPassword showPassword ->
( { state | showPassword = showPassword }
, Cmd.none
)
UpdateControl newControl ->
( { state | control = newControl }, Cmd.none )
HitEnter ->
( { state | enterCount = state.enterCount + 1 }, Cmd.none )
-- INTERNAL
type alias Id =
Int
( { state | control = newControl }
, Cmd.none
)

View File

@ -7,9 +7,11 @@ module Examples.Tooltip exposing (example, State, Msg)
-}
import Accessibility.Styled as Html exposing (Html)
import Accessibility.Styled.Key as Key
import Category exposing (Category(..))
import Css
import Debug.Control as Control exposing (Control)
import Debug.Control.Extra as ControlExtra
import Example exposing (Example)
import Html.Styled.Attributes as Attributes exposing (css, href)
import KeyboardSupport exposing (Direction(..), Key(..))
@ -18,7 +20,7 @@ import Nri.Ui.ClickableText.V3 as ClickableText
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Text.V5 as Text
import Nri.Ui.Text.V6 as Text
import Nri.Ui.Tooltip.V2 as Tooltip
import Nri.Ui.UiIcon.V1 as UiIcon
@ -32,13 +34,39 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview =
[ Html.div
[ css
[ Css.marginTop (Css.px 60)
, Css.alignSelf Css.center
]
]
[ Tooltip.view
{ id = "preview-tooltip"
, trigger =
\attributes ->
ClickableSvg.button "example-preview-tooltip-icon"
UiIcon.gear
[ ClickableSvg.custom attributes
, ClickableSvg.small
, ClickableSvg.custom [ Key.tabbable False ]
]
}
[ Tooltip.plaintext "This is a tooltip."
, Tooltip.open True
, Tooltip.onTop
, Tooltip.smallPadding
, Tooltip.fitToContent
]
]
]
, view = view
}
type alias State =
{ openTooltip : Maybe TooltipType
, staticExampleSettings : Control ExampleSettings
, staticExampleSettings : Control (List (Tooltip.Attribute Never))
}
@ -57,7 +85,7 @@ type TooltipType
type Msg
= ToggleTooltip TooltipType Bool
| SetStaticExampleSettings (Control ExampleSettings)
| SetStaticExampleSettings (Control (List (Tooltip.Attribute Never)))
update : Msg -> State -> ( State, Cmd Msg )
@ -77,13 +105,15 @@ update msg model =
view : State -> List (Html Msg)
view model =
[ Heading.h3 [] [ Html.text "Using the Tooltip module" ]
, Text.mediumBody []
[ Html.text "Label the Tooltip as either being the "
, viewPrimaryLabelTooltip model.openTooltip
, Html.text " or the "
, viewAuxillaryDescriptionToolip model.openTooltip
, Html.text " for the trigger content."
, viewToggleTip model.openTooltip
, Text.mediumBody
[ Text.html
[ Html.text "Label the Tooltip as either being the "
, viewPrimaryLabelTooltip model.openTooltip
, Html.text " or the "
, viewAuxillaryDescriptionToolip model.openTooltip
, Html.text " for the trigger content."
, viewToggleTip model.openTooltip
]
]
, viewCustomizableExample model.staticExampleSettings
]
@ -149,25 +179,15 @@ viewToggleTip openTooltip =
]
type alias ExampleSettings =
{ content : Tooltip.Attribute Never
, withoutTail : Bool
, direction : Tooltip.Attribute Never
, alignment : Tooltip.Attribute Never
, width : Tooltip.Attribute Never
, padding : Tooltip.Attribute Never
}
initStaticExampleSettings : Control ExampleSettings
initStaticExampleSettings : Control (List (Tooltip.Attribute Never))
initStaticExampleSettings =
Control.record ExampleSettings
|> Control.field "content" controlContent
|> Control.field "withoutTail" controlTail
|> Control.field "direction" controlDirection
|> Control.field "alignment" controlAlignment
|> Control.field "width" controlWidth
|> Control.field "padding" controlPadding
ControlExtra.list
|> ControlExtra.listItem "content" controlContent
|> ControlExtra.listItem "direction" controlDirection
|> ControlExtra.listItem "alignment" controlAlignment
|> ControlExtra.listItem "withoutTail" controlTail
|> ControlExtra.listItem "width" controlWidth
|> ControlExtra.listItem "padding" controlPadding
controlContent : Control (Tooltip.Attribute Never)
@ -206,9 +226,19 @@ controlContent =
]
controlTail : Control Bool
controlTail : Control (Tooltip.Attribute Never)
controlTail =
Control.bool False
Control.map
(\bool ->
if bool then
Tooltip.withoutTail
else
-- TODO: change `withoutTail` to take
-- a bool or expose a `withTail` from Tooltip.
Tooltip.css []
)
(Control.bool False)
controlDirection : Control (Tooltip.Attribute Never)
@ -253,26 +283,14 @@ controlPadding =
]
viewCustomizableExample : Control ExampleSettings -> Html Msg
viewCustomizableExample : Control (List (Tooltip.Attribute Never)) -> Html Msg
viewCustomizableExample controlSettings =
let
settings =
Control.currentValue controlSettings
attributes =
[ Tooltip.open True
, settings.content
, settings.direction
, settings.alignment
, settings.width
, settings.padding
]
++ (if settings.withoutTail then
[ Tooltip.withoutTail ]
else
[]
)
Tooltip.open True :: settings
in
Html.div []
[ Control.view SetStaticExampleSettings controlSettings

View File

@ -33,6 +33,21 @@ example =
, state = ()
, update = \_ state -> ( state, Cmd.none )
, subscriptions = \_ -> Sub.none
, preview =
IconExamples.preview
[ UiIcon.seeMore
, UiIcon.archive
, UiIcon.share
, UiIcon.footsteps
, UiIcon.person
, UiIcon.calendar
, UiIcon.missingDocument
, UiIcon.speechBalloon
, UiIcon.edit
, UiIcon.arrowTop
, UiIcon.checkmark
, UiIcon.equals
]
, view =
\_ ->
[ IconExamples.view "Interface"

View File

@ -1,6 +1,6 @@
module Main exposing (init, main)
import Accessibility.Styled as Html exposing (Html, img, text)
import Accessibility.Styled as Html exposing (Html)
import Browser exposing (Document, UrlRequest(..))
import Browser.Dom
import Browser.Navigation exposing (Key)
@ -10,15 +10,16 @@ import Css.Media exposing (withMedia)
import Dict exposing (Dict)
import Example exposing (Example)
import Examples
import Html as RootHtml
import Html.Attributes
import Html.Styled.Attributes as Attributes exposing (..)
import Html.Styled.Events as Events
import Nri.Ui.ClickableText.V3 as ClickableText
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.CssVendorPrefix.V1 as VendorPrefixed
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.MediaQuery.V1 exposing (mobile, notMobile)
import Nri.Ui.Page.V3 as Page
import Routes as Routes exposing (Route(..))
import Sort.Set as Set exposing (Set)
import Task
@ -40,6 +41,7 @@ main =
type alias Model =
{ -- Global UI
route : Route
, previousRoute : Maybe Route
, moduleStates : Dict String (Example Examples.State Examples.Msg)
, navigationKey : Key
}
@ -48,6 +50,7 @@ type alias Model =
init : () -> Url -> Key -> ( Model, Cmd Msg )
init () url key =
( { route = Routes.fromLocation url
, previousRoute = Nothing
, moduleStates =
Dict.fromList
(List.map (\example -> ( example.name, example )) Examples.all)
@ -61,6 +64,7 @@ type Msg
= UpdateModuleStates String Examples.Msg
| OnUrlRequest Browser.UrlRequest
| OnUrlChange Url
| ChangeRoute Route
| SkipToMainContent
| NoOp
@ -95,7 +99,18 @@ update action model =
( model, Browser.Navigation.load loc )
OnUrlChange route ->
( { model | route = Routes.fromLocation route }, Cmd.none )
( { model
| route = Routes.fromLocation route
, previousRoute = Just model.route
}
, Cmd.none
)
ChangeRoute route ->
( model
, Browser.Navigation.pushUrl model.navigationKey
(Routes.toString route)
)
SkipToMainContent ->
( model
@ -125,69 +140,86 @@ view_ model =
let
examples filterBy =
List.filter (\m -> filterBy m) (Dict.values model.moduleStates)
mainContentHeader heading =
Heading.h1
[ Heading.customAttr (id "maincontent")
, Heading.customAttr (tabindex -1)
, Heading.css [ marginBottom (px 30) ]
]
[ Html.text heading ]
in
case model.route of
Routes.Doodad doodad ->
case List.head (examples (\m -> m.name == doodad)) of
Just example ->
Html.main_ []
[ Example.view model.previousRoute example
|> Html.map (UpdateModuleStates example.name)
]
Nothing ->
Page.notFound
{ link = ChangeRoute Routes.All
, recoveryText = Page.ReturnTo "Component Library"
}
Routes.Category category ->
withSideNav model.route
[ mainContentHeader (Category.forDisplay category)
, examples
(\doodad ->
Set.memberOf
(Set.fromList Category.sorter doodad.categories)
category
)
|> viewPreviews (Category.forId category)
]
Routes.All ->
withSideNav model.route
[ mainContentHeader "All"
, viewPreviews "all" (examples (\_ -> True))
]
withSideNav : Route -> List (Html Msg) -> Html Msg
withSideNav currentRoute content =
Html.div
[ css
[ displayFlex
, withMedia [ mobile ] [ flexDirection column, alignItems stretch ]
, alignItems flexStart
, minHeight (vh 100)
]
]
[ navigation model.route
, Html.main_ [ css [ flexGrow (int 1), sectionStyles ] ]
(case model.route of
Routes.Doodad doodad ->
case List.head (examples (\m -> m.name == doodad)) of
Just example ->
[ mainContentHeader ("Viewing " ++ doodad ++ " doodad only")
, Html.div [ id (String.replace "." "-" example.name) ]
[ Example.view example
|> Html.map (UpdateModuleStates example.name)
]
]
Nothing ->
[ Html.text <| "Oops! We couldn't find " ++ doodad ]
Routes.Category category ->
[ mainContentHeader (Category.forDisplay category)
, examples
(\doodad ->
Set.memberOf
(Set.fromList Category.sorter doodad.categories)
category
)
|> List.map
(\example ->
Example.view example
|> Html.map (UpdateModuleStates example.name)
)
|> Html.div [ id (Category.forId category) ]
]
Routes.All ->
[ mainContentHeader "All"
, examples (\_ -> True)
|> List.map
(\example ->
Example.view example
|> Html.map (UpdateModuleStates example.name)
)
|> Html.div []
]
)
[ navigation currentRoute
, Html.main_
[ css
[ flexGrow (int 1)
, margin2 (px 40) zero
, Css.minHeight (Css.vh 100)
]
]
content
]
mainContentHeader : String -> Html msg
mainContentHeader heading =
Heading.h1
[ Heading.customAttr (id "maincontent")
, Heading.customAttr (tabindex -1)
, Heading.css [ marginBottom (px 30) ]
]
[ Html.text heading ]
viewPreviews : String -> List (Example state msg) -> Html Msg
viewPreviews containerId examples =
examples
|> List.map (\example -> Example.preview ChangeRoute example)
|> Html.div
[ id containerId
, css
[ Css.displayFlex
, Css.flexWrap Css.wrap
, Css.property "gap" "10px"
]
]
navigation : Route -> Html Msg
navigation route =
let
@ -200,31 +232,31 @@ navigation route =
False
link active hash displayName =
Html.a
[ css
[ backgroundColor transparent
, borderStyle none
, textDecoration none
ClickableText.link displayName
[ ClickableText.small
, ClickableText.css
[ Css.color Colors.navy
, Css.display Css.block
, Css.padding (Css.px 8)
, Css.borderRadius (Css.px 8)
, if active then
color Colors.navy
Css.backgroundColor Colors.glacier
else
color Colors.azure
, Fonts.baseFont
Css.batch []
]
, Attributes.href hash
, ClickableText.href hash
]
[ Html.text displayName ]
navLink category =
link (isActive category)
("#/category/" ++ Debug.toString category)
(Routes.toString (Routes.Category category))
(Category.forDisplay category)
toNavLi element =
Html.li
[ css
[ margin2 (px 10) zero
[ margin zero
, listStyle none
, textDecoration none
]
@ -269,18 +301,12 @@ navigation route =
, id "skip"
]
[ Html.text "Skip to main content" ]
, Heading.h4 [] [ Html.text "Categories" ]
, (link (route == Routes.All) "#/" "All"
:: List.map navLink Category.all
)
|> List.map toNavLi
|> Html.ul
[ css [ margin4 zero zero (px 40) zero, padding zero ]
[ css [ margin zero, padding zero ]
, id "categories"
]
]
sectionStyles : Css.Style
sectionStyles =
Css.batch [ margin2 (px 40) zero ]

View File

@ -1,4 +1,4 @@
module Routes exposing (Route(..), fromLocation)
module Routes exposing (Route(..), fromLocation, toString)
import Browser.Navigation as Navigation
import Category
@ -12,6 +12,19 @@ type Route
| All
toString : Route -> String
toString route_ =
case route_ of
Doodad exampleName ->
"#/doodad/" ++ exampleName
Category c ->
"#/category/" ++ Debug.toString c
All ->
"#/"
route : Parser Route
route =
Parser.oneOf

View File

@ -0,0 +1,19 @@
module ViewHelpers exposing (viewExamples)
import Css
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes exposing (css)
viewExamples : List ( String, Html msg ) -> Html msg
viewExamples examples =
let
viewExample ( name, view ) =
Html.tr []
[ Html.th [] [ Html.text name ]
, Html.td [] [ view ]
]
in
Html.table [ css [ Css.width (Css.pct 100) ] ]
[ Html.tbody [] (List.map viewExample examples)
]

View File

@ -1,7 +1,7 @@
module Spec.Nri.Ui.TextInput exposing (spec)
import Html.Styled
import Nri.Ui.TextInput.V6 as TextInput
import Nri.Ui.TextInput.V7 as TextInput
import Test exposing (..)
import Test.Html.Query as Query
import Test.Html.Selector exposing (..)
@ -9,13 +9,11 @@ import Test.Html.Selector exposing (..)
spec : Test
spec =
describe "Nri.Ui.TextInput.V6"
describe "Nri.Ui.TextInput.V7"
[ test "it uses the same DOM id that generateId produces" <|
\() ->
TextInput.view "myLabel"
(TextInput.text identity)
[]
""
[ TextInput.text identity ]
|> Html.Styled.toUnstyled
|> Query.fromHtml
|> Query.has

View File

@ -47,6 +47,7 @@
"Nri.Ui.SegmentedControl.V14",
"Nri.Ui.Select.V5",
"Nri.Ui.Select.V7",
"Nri.Ui.Select.V8",
"Nri.Ui.Slide.V1",
"Nri.Ui.SlideModal.V2",
"Nri.Ui.SortableTable.V2",
@ -57,9 +58,11 @@
"Nri.Ui.Tabs.V6",
"Nri.Ui.Tabs.V7",
"Nri.Ui.Text.V5",
"Nri.Ui.Text.V6",
"Nri.Ui.Text.Writing.V1",
"Nri.Ui.TextArea.V4",
"Nri.Ui.TextInput.V6",
"Nri.Ui.TextInput.V7",
"Nri.Ui.Tooltip.V1",
"Nri.Ui.Tooltip.V2",
"Nri.Ui.UiIcon.V1"