Compare commits
710 Commits
1-pager-up
...
975-dashbo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfc1f04593 | ||
|
|
eb09f846d9 | ||
|
|
d3be471413 | ||
|
|
59faa8fd42 | ||
|
|
514657e81c | ||
|
|
f54a79bdf9 | ||
|
|
cec2d7ea02 | ||
|
|
bc32c62dc6 | ||
|
|
72a2cf554a | ||
|
|
a292a98788 | ||
|
|
08a0785a17 | ||
|
|
8aed79f4e8 | ||
|
|
d1b5233095 | ||
|
|
a2f753eb15 | ||
|
|
bfebd5ce27 | ||
|
|
0c568a0519 | ||
|
|
60f8492ce0 | ||
|
|
931a48b3c6 | ||
|
|
53dc0f0870 | ||
|
|
f7f1b7fb3e | ||
|
|
19b0f8a76d | ||
|
|
45df2d0fd6 | ||
|
|
7ffd3f5031 | ||
|
|
279b1019d1 | ||
|
|
50dc153cd8 | ||
|
|
6edc20df55 | ||
|
|
5aab7d264c | ||
|
|
4a5347ccef | ||
|
|
45363d6387 | ||
|
|
ee11a048d5 | ||
|
|
666130dca0 | ||
|
|
f4ba5b83c6 | ||
|
|
d73b7dcbda | ||
|
|
efb5ca3a57 | ||
|
|
353383d323 | ||
|
|
a3c5498d6e | ||
|
|
56b3cd9fd5 | ||
|
|
0947c5d336 | ||
|
|
fa8a6aa0ba | ||
|
|
cc6efa3a3e | ||
|
|
3cc613314e | ||
|
|
1903b742d7 | ||
|
|
6297091022 | ||
|
|
42eca53f77 | ||
|
|
9cd2486942 | ||
|
|
6c4b1dc2b6 | ||
|
|
8493aaea5c | ||
|
|
13ed67b18b | ||
|
|
d35b490ee7 | ||
|
|
2356ebd7dd | ||
|
|
fea6e0a0d0 | ||
|
|
218359411d | ||
|
|
13d3ffd914 | ||
|
|
ec14996525 | ||
|
|
5fc0329c97 | ||
|
|
b124457350 | ||
|
|
b0d2de703b | ||
|
|
8fc79ce757 | ||
|
|
70de61c695 | ||
|
|
f7efceba44 | ||
|
|
ea5dd442d1 | ||
|
|
47933a236e | ||
|
|
9e48282c52 | ||
|
|
190bc3f807 | ||
|
|
7a514001ba | ||
|
|
5abce6d7e4 | ||
|
|
f11e29e7a2 | ||
|
|
d2e360d0cd | ||
|
|
d218fd2178 | ||
|
|
cdd7a2cffd | ||
|
|
97a8c04928 | ||
|
|
9c658bf020 | ||
|
|
414352ea69 | ||
|
|
5bb1eb8711 | ||
|
|
d284e6c13b | ||
|
|
928a49753c | ||
|
|
a7bf173457 | ||
|
|
d226afcb3c | ||
|
|
569be3cf74 | ||
|
|
b4578438d2 | ||
|
|
a01c5b6016 | ||
|
|
e34f16bfc6 | ||
|
|
c6444ce5cd | ||
|
|
cee2552706 | ||
|
|
93d5e0a124 | ||
|
|
e26967acac | ||
|
|
ecb2015ad5 | ||
|
|
bf10a2bc53 | ||
|
|
81f145180e | ||
|
|
beb7c19714 | ||
|
|
a116c0a0c1 | ||
|
|
f235557773 | ||
|
|
91161c8c7e | ||
|
|
9ee61806f3 | ||
|
|
50ccd994d0 | ||
|
|
350ceaaf4e | ||
|
|
fc00273b31 | ||
|
|
fa40e3d011 | ||
|
|
4f9a89d0a1 | ||
|
|
417c072382 | ||
|
|
10671b61b1 | ||
|
|
da3fa99c52 | ||
|
|
8fed8b914d | ||
|
|
8303822860 | ||
|
|
f392c67e47 | ||
|
|
fa90eb0f67 | ||
|
|
8e16afe755 | ||
|
|
86fbaee27d | ||
|
|
ff8e1b9c79 | ||
|
|
fdcd5a57e7 | ||
|
|
65e6812385 | ||
|
|
6556c1451c | ||
|
|
f42af3ae5b | ||
|
|
3cab3ac1a4 | ||
|
|
ef73d6d71e | ||
|
|
1cacd3b850 | ||
|
|
c74ab6ff27 | ||
|
|
6e527602bd | ||
|
|
479e455ab1 | ||
|
|
911552b858 | ||
|
|
c611fe38c9 | ||
|
|
ff44be5279 | ||
|
|
d10d3f7c00 | ||
|
|
8c7db5d7e1 | ||
|
|
74ace43668 | ||
|
|
3aa5f3415e | ||
|
|
4d56d4e186 | ||
|
|
d2891042b4 | ||
|
|
40de352f9e | ||
|
|
614545c1b4 | ||
|
|
984f435075 | ||
|
|
3d0460a9ee | ||
|
|
7e33178b1c | ||
|
|
723f83f164 | ||
|
|
9bc3602461 | ||
|
|
53a3f4c852 | ||
|
|
4571a8bd15 | ||
|
|
e55a9273bf | ||
|
|
ad0ed0d6fb | ||
|
|
425f8eb742 | ||
|
|
d0fe4de3ac | ||
|
|
a6ea9fd25c | ||
|
|
9959f53956 | ||
|
|
ce21df6048 | ||
|
|
8432ac2334 | ||
|
|
2178659d31 | ||
|
|
2bd38182d2 | ||
|
|
84a05994b2 | ||
|
|
e4c97e925f | ||
|
|
077b1b8ed9 | ||
|
|
bc6f13dc06 | ||
|
|
450be48e9c | ||
|
|
e7a3b98fff | ||
|
|
f51d4be8de | ||
|
|
6a91627c6b | ||
|
|
f922331040 | ||
|
|
3ad901278d | ||
|
|
5d97856a81 | ||
|
|
654b406827 | ||
|
|
8d6c99e5ee | ||
|
|
654557e793 | ||
|
|
36987e7202 | ||
|
|
6cee00929e | ||
|
|
06a15c5afc | ||
|
|
b58b517fc8 | ||
|
|
492886d0ae | ||
|
|
da0c3ab306 | ||
|
|
95295c1c86 | ||
|
|
0b4981b25c | ||
|
|
a37b2f8281 | ||
|
|
969ecb39af | ||
|
|
bebe974c8a | ||
|
|
83dfdc31f2 | ||
|
|
31fb43e45c | ||
|
|
22d728f0f8 | ||
|
|
040f4b4f9f | ||
|
|
009773eb1b | ||
|
|
b1bd51730d | ||
|
|
d1682c641d | ||
|
|
bc3928cb0f | ||
|
|
68701f8022 | ||
|
|
bda164f2a1 | ||
|
|
6f9e55cba0 | ||
|
|
853af1040e | ||
|
|
6df7770ee1 | ||
|
|
7e12e6d488 | ||
|
|
1f7ce11741 | ||
|
|
be0ea767f7 | ||
|
|
7c491e87ea | ||
|
|
2b45f99294 | ||
|
|
008b990e14 | ||
|
|
91aca2ec4e | ||
|
|
b50bdf2f21 | ||
|
|
6e73ee0bc9 | ||
|
|
4330fbe493 | ||
|
|
5d8f3e43ea | ||
|
|
191700f8ec | ||
|
|
6e19fab1d5 | ||
|
|
9398babd6f | ||
|
|
534d6459b8 | ||
|
|
f09d7ce537 | ||
|
|
0e0b00ef4b | ||
|
|
75db3a34c0 | ||
|
|
381315d18a | ||
|
|
5f41eb2ccc | ||
|
|
5e7877bd78 | ||
|
|
26031cb1b2 | ||
|
|
5da110ded4 | ||
|
|
e5403a52f1 | ||
|
|
b87022af8e | ||
|
|
591961413a | ||
|
|
af119aad1c | ||
|
|
d26a5b6a75 | ||
|
|
62a09c5223 | ||
|
|
83767b8ba8 | ||
|
|
81f4b1436d | ||
|
|
2fbda1cd0c | ||
|
|
febbcd154c | ||
|
|
63a4a92657 | ||
|
|
e48b4506de | ||
|
|
e30f017558 | ||
|
|
073def2510 | ||
|
|
c21d093c61 | ||
|
|
3b2866bda4 | ||
|
|
472e6d48a4 | ||
|
|
745aaee1d7 | ||
|
|
1a00eeaed6 | ||
|
|
35e200b328 | ||
|
|
14b81ce908 | ||
|
|
20cddeb852 | ||
|
|
b90057f84c | ||
|
|
25dcdd11c7 | ||
|
|
cbfccb774e | ||
|
|
f54533da83 | ||
|
|
11890d3219 | ||
|
|
415a1061fa | ||
|
|
40794f816a | ||
|
|
7f56c62a6d | ||
|
|
a3022dcfe6 | ||
|
|
b538d25b13 | ||
|
|
4a98080c3f | ||
|
|
09c4dd2e1d | ||
|
|
7ba937b14a | ||
|
|
2131692449 | ||
|
|
600114fa4f | ||
|
|
1d790868f3 | ||
|
|
0a11211c81 | ||
|
|
dfe9a46890 | ||
|
|
f4048d5b55 | ||
|
|
c695b59385 | ||
|
|
0cc91b43a9 | ||
|
|
622bdd4368 | ||
|
|
fb3c9f412a | ||
|
|
e055318ca8 | ||
|
|
d9c0c751ab | ||
|
|
a542fa916f | ||
|
|
88f0715b2c | ||
|
|
899027bb04 | ||
|
|
b487a915f6 | ||
|
|
8c9acad081 | ||
|
|
bd2ea81081 | ||
|
|
90025ded54 | ||
|
|
c290e60a6c | ||
|
|
222c83ebbc | ||
|
|
5304c0f7a9 | ||
|
|
1271098ff9 | ||
|
|
782cff2d5f | ||
|
|
7ca258b547 | ||
|
|
1d1e7747d0 | ||
|
|
1ad3d875c4 | ||
|
|
86ac810fff | ||
|
|
f31a78cbcd | ||
|
|
df3cdaf93e | ||
|
|
588245f9f5 | ||
|
|
2c512181c2 | ||
|
|
c46a6a5f86 | ||
|
|
962fce3c7b | ||
|
|
1c75f9abeb | ||
|
|
195af626c6 | ||
|
|
a34ab8c972 | ||
|
|
07f9255e36 | ||
|
|
167023c7a7 | ||
|
|
3106b85e08 | ||
|
|
17100a24b8 | ||
|
|
85f0a31505 | ||
|
|
7163f813ce | ||
|
|
35ecd7006a | ||
|
|
f5a5fbc926 | ||
|
|
9a4afe44bb | ||
|
|
d2411e6cbe | ||
|
|
9031d0cb97 | ||
|
|
acb70b43cd | ||
|
|
72c8a46b8c | ||
|
|
23f894581c | ||
|
|
06fab2dd03 | ||
|
|
95ba7c3099 | ||
|
|
47695b97d3 | ||
|
|
73590840ee | ||
|
|
988c1e93a1 | ||
|
|
3e7a4f78f3 | ||
|
|
86a19c945e | ||
|
|
a46a21a519 | ||
|
|
4fff35590b | ||
|
|
d4d36087d4 | ||
|
|
4effd7df06 | ||
|
|
d12faf6cd8 | ||
|
|
27ff29d0db | ||
|
|
ceedff4d0e | ||
|
|
a2ece1554c | ||
|
|
d55b614b2b | ||
|
|
b94fb8a867 | ||
|
|
8445906fa1 | ||
|
|
76f9c5e6ab | ||
|
|
ce9fdc693f | ||
|
|
db201eeda9 | ||
|
|
5d0eb7d211 | ||
|
|
0ef554674a | ||
|
|
7ec9a5a4f8 | ||
|
|
4c833a4360 | ||
|
|
57ec11d7c5 | ||
|
|
9e47062aa7 | ||
|
|
d34249ff8b | ||
|
|
f1cdb79475 | ||
|
|
124d342b39 | ||
|
|
3a4e3889c0 | ||
|
|
af2da54ffc | ||
|
|
fd196c6534 | ||
|
|
afe6e9c11a | ||
|
|
643e1ef1b6 | ||
|
|
838f643b18 | ||
|
|
206165499f | ||
|
|
fefa8288c4 | ||
|
|
154b8fd8f3 | ||
|
|
d6205bc1ff | ||
|
|
cd508c05b0 | ||
|
|
48c20c7966 | ||
|
|
b255e2a112 | ||
|
|
e8ed0ef8b0 | ||
|
|
efce22b171 | ||
|
|
93ac55ce35 | ||
|
|
00db493305 | ||
|
|
c7e05246b1 | ||
|
|
20e8aaac1a | ||
|
|
020dc871f7 | ||
|
|
64094c02ed | ||
|
|
f661f2cba4 | ||
|
|
fab8d92651 | ||
|
|
5c4816ddbe | ||
|
|
38379176f4 | ||
|
|
57f2ee798b | ||
|
|
5557f313b3 | ||
|
|
d164fe7049 | ||
|
|
c48cb0d020 | ||
|
|
924bd41b7c | ||
|
|
8ab49f72ae | ||
|
|
d7e7a082cd | ||
|
|
25190117dc | ||
|
|
9846379c0f | ||
|
|
8aa82a1f49 | ||
|
|
6774ae9194 | ||
|
|
13b0f05c1d | ||
|
|
299b9a5062 | ||
|
|
1dff2e5e1a | ||
|
|
4d87636805 | ||
|
|
285a54a552 | ||
|
|
40235905e2 | ||
|
|
e2ab8ea008 | ||
|
|
2675099664 | ||
|
|
1d66533111 | ||
|
|
75f5224019 | ||
|
|
492bc7b355 | ||
|
|
75f820e75b | ||
|
|
aa6d54a353 | ||
|
|
5bc752a826 | ||
|
|
6e082a352d | ||
|
|
b51f3f73fe | ||
|
|
811c51c469 | ||
|
|
d8f930e3d5 | ||
|
|
5c40f826f2 | ||
|
|
acb0e76517 | ||
|
|
02891b9b36 | ||
|
|
bb2d0a375e | ||
|
|
c069ab7abc | ||
|
|
54eb8df9f7 | ||
|
|
e3490691d3 | ||
|
|
4753be56c2 | ||
|
|
1c657ff170 | ||
|
|
b5cac7bfb1 | ||
|
|
ab71be0246 | ||
|
|
429bbe2e87 | ||
|
|
10d207eac4 | ||
|
|
5683c50cc4 | ||
|
|
3110189402 | ||
|
|
d02d2de9dc | ||
|
|
a752566e2f | ||
|
|
594e1095f7 | ||
|
|
c00f769e44 | ||
|
|
c94da180cc | ||
|
|
15123f06f9 | ||
|
|
118fd13189 | ||
|
|
bffd013702 | ||
|
|
d4500461d0 | ||
|
|
8e158e7646 | ||
|
|
3ed4af79c7 | ||
|
|
f075d28799 | ||
|
|
a0e139f14b | ||
|
|
dedb41f67e | ||
|
|
068663ec1a | ||
|
|
e22386e91b | ||
|
|
f16c7b6699 | ||
|
|
0db9fda4b3 | ||
|
|
06f7283a32 | ||
|
|
33e82ed302 | ||
|
|
040221b7c6 | ||
|
|
bbf41bae5e | ||
|
|
5c53b8789e | ||
|
|
d856e39484 | ||
|
|
0218ffd9ba | ||
|
|
5e1a224c29 | ||
|
|
e2b82991af | ||
|
|
731f571d76 | ||
|
|
c975f57c95 | ||
|
|
c192e9f53f | ||
|
|
91f1790c89 | ||
|
|
f257a38084 | ||
|
|
6c1a5687e8 | ||
|
|
fe259b491d | ||
|
|
ddaee26074 | ||
|
|
5f84cd1bbe | ||
|
|
50f6a66542 | ||
|
|
21e5c7d103 | ||
|
|
1d9239aa13 | ||
|
|
a1974a251e | ||
|
|
f433d69596 | ||
|
|
2a641d0d4e | ||
|
|
daaa04b292 | ||
|
|
35f80589c1 | ||
|
|
c890d12a26 | ||
|
|
b164d175a8 | ||
|
|
f87d717425 | ||
|
|
bb9bd9872e | ||
|
|
b85a26e6be | ||
|
|
bfb49d44ae | ||
|
|
851505c906 | ||
|
|
51e2da1be0 | ||
|
|
bfe8c74ce2 | ||
|
|
f0cfb51441 | ||
|
|
1d397852b3 | ||
|
|
7e4cdd23a1 | ||
|
|
f23f13f201 | ||
|
|
eec791265c | ||
|
|
3a94826ad6 | ||
|
|
96452f8338 | ||
|
|
f69d7d106e | ||
|
|
f83056851e | ||
|
|
611ebc48d1 | ||
|
|
3909bfd1c4 | ||
|
|
99436f65f9 | ||
|
|
e5e0cf5dee | ||
|
|
30439bd3d9 | ||
|
|
6596b7e89c | ||
|
|
94152b51ef | ||
|
|
5a7c19ed22 | ||
|
|
6bd98188f9 | ||
|
|
4b8ad438f2 | ||
|
|
c6e356c315 | ||
|
|
5a61dccba0 | ||
|
|
ce54e6d73c | ||
|
|
93e4d1b4bb | ||
|
|
e155f8d392 | ||
|
|
705808e0e5 | ||
|
|
42cd3b4a0e | ||
|
|
2da3c0de4f | ||
|
|
b3efee251a | ||
|
|
5807c447dd | ||
|
|
239af57440 | ||
|
|
227809a328 | ||
|
|
f813a403a9 | ||
|
|
d120b70f0c | ||
|
|
31919294ad | ||
|
|
2ff8a08fee | ||
|
|
c7de9dcf6a | ||
|
|
863625de6a | ||
|
|
29ea098fd3 | ||
|
|
92953676c4 | ||
|
|
cc8f8dfcc2 | ||
|
|
1507199e6b | ||
|
|
559965c479 | ||
|
|
8712d7d822 | ||
|
|
3c9d6bd56c | ||
|
|
43743e1a47 | ||
|
|
3490507c3b | ||
|
|
97a6de68b8 | ||
|
|
149a497d9c | ||
|
|
e6b081b9e5 | ||
|
|
d8a6004926 | ||
|
|
5e783318b8 | ||
|
|
e8d7d91402 | ||
|
|
67c8bef7aa | ||
|
|
443315d3a5 | ||
|
|
f82d77cdaf | ||
|
|
c19083b04e | ||
|
|
b5d795e09e | ||
|
|
1881cd7c89 | ||
|
|
b1c6a768ec | ||
|
|
155ea99b36 | ||
|
|
0d2f125140 | ||
|
|
32083dff99 | ||
|
|
2675760f4d | ||
|
|
ea6d49e35b | ||
|
|
84d436baf2 | ||
|
|
42c4670c89 | ||
|
|
b04f0dea54 | ||
|
|
c7b756e46c | ||
|
|
7a100a68f2 | ||
|
|
c02b7b0047 | ||
|
|
fea6debaf2 | ||
|
|
38c6ddf0bc | ||
|
|
3cee5302e4 | ||
|
|
2e16d8d31b | ||
|
|
40dd67056d | ||
|
|
68f6145ca5 | ||
|
|
c3e4114b3e | ||
|
|
df754c34ed | ||
|
|
787c46e7b0 | ||
|
|
51b6c38e2f | ||
|
|
8ab550a22f | ||
|
|
34e52f4887 | ||
|
|
bbc84fc095 | ||
|
|
522c99cb0b | ||
|
|
a44afb0bcc | ||
|
|
8c9721d8e2 | ||
|
|
c6eb007386 | ||
|
|
8b3807da3c | ||
|
|
64256a6f57 | ||
|
|
8ea03c9da6 | ||
|
|
a388178e2a | ||
|
|
1463d8421b | ||
|
|
7743c2fe79 | ||
|
|
fc20dbc842 | ||
|
|
769f1e9355 | ||
|
|
aa0f1b39cf | ||
|
|
bdb363daf8 | ||
|
|
6c6c22c307 | ||
|
|
be39bc9692 | ||
|
|
ea40b59643 | ||
|
|
5e4faf2b0a | ||
|
|
cd8c11e66c | ||
|
|
d79e15c2a1 | ||
|
|
b98909727f | ||
|
|
476644369d | ||
|
|
a9ee0769ee | ||
|
|
412fad89ff | ||
|
|
4aa781ed9a | ||
|
|
966e929d47 | ||
|
|
f47deb1f99 | ||
|
|
3109476637 | ||
|
|
8f4fd494c5 | ||
|
|
c597b2d898 | ||
|
|
a34d6f139f | ||
|
|
8eba976ec1 | ||
|
|
266416f07b | ||
|
|
88236f3ff0 | ||
|
|
feebdd2f59 | ||
|
|
d31905c8ad | ||
|
|
9fc44a352b | ||
|
|
57b0a675ee | ||
|
|
9f62313332 | ||
|
|
bf338368a1 | ||
|
|
a4fc40bcba | ||
|
|
502af3b8fe | ||
|
|
0829fa2fad | ||
|
|
f9c95eee4a | ||
|
|
864c3dd6d9 | ||
|
|
d9924ed857 | ||
|
|
5b8b235cb6 | ||
|
|
96d9e009af | ||
|
|
51fde90f79 | ||
|
|
77271df2a8 | ||
|
|
cc0e0a90f0 | ||
|
|
a574fc7b3a | ||
|
|
5ada4a5200 | ||
|
|
93cc86b919 | ||
|
|
0d2d11282a | ||
|
|
88d55242a6 | ||
|
|
9d16738c83 | ||
|
|
031a643b26 | ||
|
|
01a319e0be | ||
|
|
b3db38e988 | ||
|
|
e44e843614 | ||
|
|
bad2fd5fe4 | ||
|
|
d66ff27bae | ||
|
|
6b22c347da | ||
|
|
01c18c9624 | ||
|
|
e472282769 | ||
|
|
bc04bccdca | ||
|
|
1a34c5f6ce | ||
|
|
26a454122c | ||
|
|
97dff0a92c | ||
|
|
9e609efa1c | ||
|
|
da4a78a7e2 | ||
|
|
ff33c7c7f9 | ||
|
|
ffafc16430 | ||
|
|
68d49fc8dd | ||
|
|
0929a920b7 | ||
|
|
1be305d045 | ||
|
|
a85b98ecb5 | ||
|
|
e4dad627df | ||
|
|
bb1c90ff34 | ||
|
|
7e7f4245a4 | ||
|
|
fc9667d25b | ||
|
|
348e29fb40 | ||
|
|
621c381de8 | ||
|
|
c0915a619f | ||
|
|
15745475b9 | ||
|
|
d36c70ed16 | ||
|
|
5afecc2210 | ||
|
|
b92f386290 | ||
|
|
382d263972 | ||
|
|
00d9b324dc | ||
|
|
08fef34c5e | ||
|
|
47ab69c4ec | ||
|
|
4dd3679c4f | ||
|
|
1ddc821dd1 | ||
|
|
1a1ca0a7c9 | ||
|
|
7637695417 | ||
|
|
4897658fd0 | ||
|
|
d5f9fb15d7 | ||
|
|
9c384c1183 | ||
|
|
5314d82799 | ||
|
|
ec14370b5a | ||
|
|
d9e5709a0f | ||
|
|
d430bea3ee | ||
|
|
0675749b0c | ||
|
|
74c1bb3baf | ||
|
|
0188bf1cf2 | ||
|
|
41d1d055a4 | ||
|
|
ab92764840 | ||
|
|
6972290064 | ||
|
|
8438c59064 | ||
|
|
9dc81b101b | ||
|
|
61f6bbd0e8 | ||
|
|
da57b109fc | ||
|
|
1dc8d6ee53 | ||
|
|
a030c0aaca | ||
|
|
fdd35e8c6b | ||
|
|
a2db4b5738 | ||
|
|
e1528a27b2 | ||
|
|
196f160696 | ||
|
|
0c5090c94b | ||
|
|
be5144b99a | ||
|
|
abf06247d2 | ||
|
|
242c7acd34 | ||
|
|
817ebbe923 | ||
|
|
eec2caa6e4 | ||
|
|
27a193160f | ||
|
|
b5664c3d20 | ||
|
|
e3e11327da | ||
|
|
34fef6954c | ||
|
|
08d612166a | ||
|
|
185f98512b | ||
|
|
b9736f4cf6 | ||
|
|
18a53b972a | ||
|
|
4aaf73ca7a | ||
|
|
39e7ba1843 | ||
|
|
69598afa0c | ||
|
|
f188dea3cd | ||
|
|
6d9b60042a | ||
|
|
11687479d5 | ||
|
|
3a463737c7 | ||
|
|
559ed217e4 | ||
|
|
dc342385ce | ||
|
|
8280dadc35 | ||
|
|
71f3ae2316 | ||
|
|
8b6a4d9e48 | ||
|
|
adb8229dae | ||
|
|
80da9873da | ||
|
|
10977b310d | ||
|
|
5f7e1cf0dd | ||
|
|
4642c0fa58 | ||
|
|
a6c75d4123 | ||
|
|
9e323e9477 | ||
|
|
933a4808e4 | ||
|
|
6188f7bc74 | ||
|
|
773298fa54 | ||
|
|
9800cd0879 | ||
|
|
ccca301f8d | ||
|
|
4c004cbc50 | ||
|
|
10e4e6a973 | ||
|
|
28220a0c79 | ||
|
|
7f522b918c | ||
|
|
ec5cd10511 | ||
|
|
0eb91a4294 | ||
|
|
b63192ecaa | ||
|
|
8d00f2269a | ||
|
|
d2a27ff4c5 | ||
|
|
8840869aee | ||
|
|
9d7adf4416 | ||
|
|
7e4ec44003 | ||
|
|
c07806d3dd | ||
|
|
68c784c202 | ||
|
|
027fca16b8 | ||
|
|
a4adb2e8aa | ||
|
|
200c3c6315 | ||
|
|
5d5d25d6d3 | ||
|
|
4fd472182d | ||
|
|
f02a14e5d0 | ||
|
|
3e764fe5a6 | ||
|
|
fefdb54665 | ||
|
|
414d6621bf |
4
.github/actions/setup-project/action.yml
vendored
@@ -13,9 +13,9 @@ runs:
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.9"
|
||||
- name: Install pipenv
|
||||
- name: Install poetry
|
||||
shell: bash
|
||||
run: pip install --upgrade pipenv
|
||||
run: pip install poetry
|
||||
- name: Install application dependencies
|
||||
shell: bash
|
||||
run: make bootstrap
|
||||
|
||||
47
.github/workflows/checks.yml
vendored
@@ -33,17 +33,40 @@ jobs:
|
||||
annotations: failed-tests
|
||||
prnumber: ${{ steps.findPr.outputs.number }}
|
||||
- name: Run style checks
|
||||
run: pipenv run flake8 .
|
||||
run: poetry run flake8 .
|
||||
- name: Check imports alphabetized
|
||||
run: pipenv run isort --check-only ./app ./tests
|
||||
run: poetry run isort --check-only ./app ./tests
|
||||
- name: Check dead code
|
||||
run: make dead-code
|
||||
- name: Run js lint
|
||||
run: npm run lint
|
||||
- name: Run js tests
|
||||
run: npm test
|
||||
- name: Run py tests with coverage
|
||||
run: pipenv run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10
|
||||
run: poetry run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 --ignore=tests/end_to_end tests/
|
||||
# - name: Run E2E tests
|
||||
# run: poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end
|
||||
# env:
|
||||
# NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
||||
# NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||
# NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||
# NOTIFY_E2E_TEST_URI: ${{ secrets.NOTIFY_E2E_TEST_URI }}
|
||||
- name: Check coverage threshold
|
||||
run: pipenv run coverage report --fail-under=90
|
||||
run: poetry run coverage report --fail-under=90
|
||||
# - name: Health check
|
||||
# run: |
|
||||
# response=$(curl -url ${{secrets.NOTIFY_E2E_TEST_URI}}_status)
|
||||
# if grep -q "ok" <<< "$response"; then
|
||||
# echo "Health check passed"
|
||||
# else
|
||||
# echo "Health check failed"
|
||||
# exit 1
|
||||
# fi
|
||||
# env:
|
||||
# NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
||||
# NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||
# NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||
# NOTIFY_E2E_TEST_URI: ${{ secrets.NOTIFY_E2E_TEST_URI }}
|
||||
|
||||
validate-new-relic-config:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -56,7 +79,7 @@ jobs:
|
||||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||
# Need to set a NEW_RELIC_ENVIRONMENT with monitor_mode: true
|
||||
NEW_RELIC_ENVIRONMENT: staging
|
||||
run: pipenv run newrelic-admin validate-config $NEW_RELIC_CONFIG_FILE
|
||||
run: poetry run newrelic-admin validate-config $NEW_RELIC_CONFIG_FILE
|
||||
|
||||
dependency-audits:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -64,7 +87,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup-project
|
||||
- name: Create requirements.txt
|
||||
run: pipenv requirements > requirements.txt
|
||||
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
|
||||
- uses: pypa/gh-action-pip-audit@v1.0.6
|
||||
with:
|
||||
inputs: requirements.txt
|
||||
@@ -77,7 +100,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup-project
|
||||
- name: Run scan
|
||||
run: pipenv run bandit -r app/ --confidence-level medium
|
||||
run: poetry run bandit -r app/ --confidence-level medium
|
||||
|
||||
dynamic-scan:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -89,14 +112,14 @@ jobs:
|
||||
env:
|
||||
NOTIFY_ENVIRONMENT: scanning
|
||||
- name: Run OWASP Baseline Scan
|
||||
uses: zaproxy/action-baseline@v0.7.0
|
||||
uses: zaproxy/action-baseline@v0.9.0
|
||||
with:
|
||||
docker_name: "owasp/zap2docker-stable"
|
||||
target: "http://localhost:6012"
|
||||
docker_name: 'ghcr.io/zaproxy/zaproxy:weekly'
|
||||
target: 'http://localhost:6012'
|
||||
fail_action: true
|
||||
allow_issue_writing: false
|
||||
rules_file_name: "zap.conf"
|
||||
cmd_options: "-I"
|
||||
rules_file_name: 'zap.conf'
|
||||
cmd_options: '-I'
|
||||
|
||||
a11y-scan:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
8
.github/workflows/daily_checks.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup-project
|
||||
- name: Create requirements.txt
|
||||
run: pipenv requirements > requirements.txt
|
||||
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
|
||||
- uses: pypa/gh-action-pip-audit@v1.0.6
|
||||
with:
|
||||
inputs: requirements.txt
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup-project
|
||||
- name: Run scan
|
||||
run: pipenv run bandit -r app/ --confidence-level medium
|
||||
run: poetry run bandit -r app/ --confidence-level medium
|
||||
|
||||
dynamic-scan:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -50,9 +50,9 @@ jobs:
|
||||
env:
|
||||
NOTIFY_ENVIRONMENT: scanning
|
||||
- name: Run OWASP Full Scan
|
||||
uses: zaproxy/action-full-scan@v0.4.0
|
||||
uses: zaproxy/action-full-scan@v0.7.0
|
||||
with:
|
||||
docker_name: 'owasp/zap2docker-stable'
|
||||
docker_name: 'ghcr.io/zaproxy/zaproxy:weekly'
|
||||
target: 'http://localhost:6012'
|
||||
fail_action: true
|
||||
allow_issue_writing: false
|
||||
|
||||
11
.github/workflows/deploy-demo.yml
vendored
@@ -43,8 +43,8 @@ jobs:
|
||||
|
||||
- uses: ./.github/actions/setup-project
|
||||
|
||||
- name: Create requirements.txt because Cloud Foundry does a weird pipenv thing
|
||||
run: pipenv requirements > requirements.txt
|
||||
- name: Create requirements.txt
|
||||
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
|
||||
|
||||
- name: Deploy to cloud.gov
|
||||
uses: 18f/cg-deploy-action@main
|
||||
@@ -52,13 +52,13 @@ jobs:
|
||||
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
|
||||
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
|
||||
BASIC_AUTH_PASSWORD: ${{ secrets.BASIC_AUTH_PASSWORD }}
|
||||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
|
||||
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
|
||||
with:
|
||||
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
|
||||
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
||||
cf_org: gsa-tts-benefits-studio-prototyping
|
||||
cf_org: gsa-tts-benefits-studio
|
||||
cf_space: notify-demo
|
||||
push_arguments: >-
|
||||
--vars-file deploy-config/demo.yml
|
||||
@@ -66,10 +66,9 @@ jobs:
|
||||
--var SECRET_KEY="$SECRET_KEY"
|
||||
--var ADMIN_CLIENT_USERNAME="notify-admin"
|
||||
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
|
||||
--var BASIC_AUTH_USERNAME="curiousabout"
|
||||
--var BASIC_AUTH_PASSWORD="$BASIC_AUTH_PASSWORD"
|
||||
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
|
||||
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
|
||||
--var LOGIN_PEM="$LOGIN_PEM"
|
||||
|
||||
- name: Check for changes to egress config
|
||||
id: changed-egress-config
|
||||
|
||||
12
.github/workflows/deploy-prod.yml
vendored
@@ -43,8 +43,8 @@ jobs:
|
||||
|
||||
- uses: ./.github/actions/setup-project
|
||||
|
||||
- name: Create requirements.txt because Cloud Foundry does a weird pipenv thing
|
||||
run: pipenv requirements > requirements.txt
|
||||
- name: Create requirements.txt
|
||||
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
|
||||
|
||||
- name: Deploy to cloud.gov
|
||||
uses: 18f/cg-deploy-action@main
|
||||
@@ -52,13 +52,14 @@ jobs:
|
||||
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
|
||||
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
|
||||
BASIC_AUTH_PASSWORD: ${{ secrets.BASIC_AUTH_PASSWORD }}
|
||||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
|
||||
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
|
||||
|
||||
with:
|
||||
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
|
||||
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
||||
cf_org: gsa-tts-benefits-studio-prototyping
|
||||
cf_org: gsa-tts-benefits-studio
|
||||
cf_space: notify-production
|
||||
push_arguments: >-
|
||||
--vars-file deploy-config/production.yml
|
||||
@@ -66,10 +67,9 @@ jobs:
|
||||
--var SECRET_KEY="$SECRET_KEY"
|
||||
--var ADMIN_CLIENT_USERNAME="notify-admin"
|
||||
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
|
||||
--var BASIC_AUTH_USERNAME="curiousabout"
|
||||
--var BASIC_AUTH_PASSWORD="$BASIC_AUTH_PASSWORD"
|
||||
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
|
||||
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
|
||||
--var LOGIN_PEM="$LOGIN_PEM"
|
||||
|
||||
- name: Check for changes to egress config
|
||||
id: changed-egress-config
|
||||
|
||||
27
.github/workflows/deploy.yml
vendored
@@ -48,8 +48,9 @@ jobs:
|
||||
|
||||
- uses: ./.github/actions/setup-project
|
||||
|
||||
- name: Create requirements.txt because Cloud Foundry does a weird pipenv thing
|
||||
run: pipenv requirements > requirements.txt
|
||||
- name: Create requirements.txt
|
||||
run: poetry export --without-hashes --format=requirements.txt > requirements.txt
|
||||
|
||||
|
||||
- name: Deploy to cloud.gov
|
||||
uses: 18f/cg-deploy-action@main
|
||||
@@ -57,13 +58,20 @@ jobs:
|
||||
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
|
||||
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
|
||||
BASIC_AUTH_PASSWORD: ${{ secrets.BASIC_AUTH_PASSWORD }}
|
||||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
|
||||
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
|
||||
LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov"
|
||||
LOGIN_DOT_GOV_USER_INFO_URL: "https://idp.int.identitysandbox.gov/api/openid_connect/userinfo"
|
||||
LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://idp.int.identitysandbox.gov/api/openid_connect/token"
|
||||
LOGIN_DOT_GOV_LOGOUT_URL: "https://idp.int.identitysandbox.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-staging.app.cloud.gov/sign-out"
|
||||
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://idp.int.identitysandbox.gov/openid_connect/logout?"
|
||||
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-staging.app.cloud.gov/sign-out"
|
||||
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://idp.int.identitysandbox.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop"
|
||||
with:
|
||||
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
|
||||
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
||||
cf_org: gsa-tts-benefits-studio-prototyping
|
||||
cf_org: gsa-tts-benefits-studio
|
||||
cf_space: notify-staging
|
||||
push_arguments: >-
|
||||
--vars-file deploy-config/staging.yml
|
||||
@@ -71,10 +79,17 @@ jobs:
|
||||
--var SECRET_KEY="$SECRET_KEY"
|
||||
--var ADMIN_CLIENT_USERNAME="notify-admin"
|
||||
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
|
||||
--var BASIC_AUTH_USERNAME="curiousabout"
|
||||
--var BASIC_AUTH_PASSWORD="$BASIC_AUTH_PASSWORD"
|
||||
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
|
||||
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
|
||||
--var LOGIN_PEM="$LOGIN_PEM"
|
||||
--var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID"
|
||||
--var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL"
|
||||
--var LOGIN_DOT_GOV_ACCESS_TOKEN_URL="$LOGIN_DOT_GOV_ACCESS_TOKEN_URL"
|
||||
--var LOGIN_DOT_GOV_LOGOUT_URL="$LOGIN_DOT_GOV_LOGOUT_URL"
|
||||
--var LOGIN_DOT_GOV_BASE_LOGOUT_URL="$LOGIN_DOT_GOV_BASE_LOGOUT_URL"
|
||||
--var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT"
|
||||
--var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL"
|
||||
|
||||
|
||||
- name: Check for changes to egress config
|
||||
id: changed-egress-config
|
||||
|
||||
6
.gitignore
vendored
@@ -123,3 +123,9 @@ app/templates/vendor
|
||||
secrets.auto.tfvars
|
||||
terraform.tfstate
|
||||
terraform.tfstate.backup
|
||||
|
||||
# Playwright
|
||||
playwright/
|
||||
|
||||
# Pyenv
|
||||
.python-version
|
||||
|
||||
4
.pa11yci
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"defaults": {
|
||||
"standard": "WCAG2AA",
|
||||
"runners": ["axe"],
|
||||
"runners": ["htmlcs"],
|
||||
"concurrency": 1,
|
||||
"hideElements": [
|
||||
"nav > ol a",
|
||||
"#sms-pricing-details",
|
||||
"a.govuk-footer__link.govuk-footer__copyright-logo"
|
||||
"a.usa-footer__link.usa-footer__copyright-logo"
|
||||
]
|
||||
},
|
||||
"urls": [
|
||||
|
||||
13
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- id: debug-statements
|
||||
- id: check-merge-conflict
|
||||
- id: check-toml
|
||||
@@ -1 +0,0 @@
|
||||
3.9
|
||||
69
Makefile
@@ -15,7 +15,11 @@ NVMSH := $(shell [ -f "$(HOME)/.nvm/nvm.sh" ] && echo "$(HOME)/.nvm/nvm.sh" || e
|
||||
|
||||
.PHONY: bootstrap
|
||||
bootstrap: generate-version-file ## Set up everything to run the app
|
||||
pipenv install --dev
|
||||
poetry self update
|
||||
poetry self add poetry-dotenv-plugin
|
||||
poetry install --sync --no-root
|
||||
poetry run playwright install --with-deps
|
||||
poetry run pre-commit install
|
||||
source $(NVMSH) --no-use && nvm install && npm ci --no-audit
|
||||
source $(NVMSH) && npm run build
|
||||
|
||||
@@ -25,10 +29,10 @@ watch-frontend: ## Build frontend and watch for changes
|
||||
|
||||
.PHONY: run-flask
|
||||
run-flask: ## Run flask
|
||||
pipenv run newrelic-admin run-program flask run -p 6012 --host=0.0.0.0
|
||||
poetry run newrelic-admin run-program flask run -p 6012 --host=0.0.0.0
|
||||
|
||||
.PHONY: run-flask-bare
|
||||
run-flask-bare: ## Run flask without invoking pipenv so we can override ENV variables in .env
|
||||
run-flask-bare: ## Run flask without invoking poetry so we can override ENV variables in .env
|
||||
flask run -p 6012 --host=0.0.0.0
|
||||
|
||||
.PHONY: npm-audit
|
||||
@@ -47,16 +51,37 @@ generate-version-file: ## Generates the app version file
|
||||
test: py-lint py-test js-lint js-test ## Run tests
|
||||
|
||||
.PHONY: py-lint
|
||||
py-lint: ## Run python linting scanners
|
||||
pipenv run flake8 .
|
||||
pipenv run isort --check-only ./app ./tests
|
||||
py-lint: ## Run python linting scanners and black
|
||||
poetry self add poetry-dotenv-plugin
|
||||
poetry run black .
|
||||
poetry run flake8 .
|
||||
poetry run isort --check-only ./app ./tests
|
||||
|
||||
.PHONY: avg-complexity
|
||||
avg-complexity:
|
||||
echo "*** Shows average complexity in radon of all code ***"
|
||||
poetry run radon cc ./app -a -na
|
||||
|
||||
.PHONY: too-complex
|
||||
too-complex:
|
||||
echo "*** Shows code that got a rating of C, D or F in radon ***"
|
||||
poetry run radon cc ./app -a -nc
|
||||
|
||||
.PHONY: py-test
|
||||
py-test: export NEW_RELIC_ENVIRONMENT=test
|
||||
py-test: ## Run python unit tests
|
||||
pipenv run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 tests/
|
||||
pipenv run coverage report --fail-under=90
|
||||
pipenv run coverage html -d .coverage_cache
|
||||
poetry run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 --ignore=tests/end_to_end tests/
|
||||
poetry run coverage report --fail-under=96
|
||||
poetry run coverage html -d .coverage_cache
|
||||
|
||||
.PHONY: dead-code
|
||||
dead-code:
|
||||
poetry run vulture ./app --min-confidence=100
|
||||
|
||||
.PHONY: e2e-test
|
||||
e2e-test: export NEW_RELIC_ENVIRONMENT=test
|
||||
e2e-test: ## Run end-to-end integration tests
|
||||
poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end
|
||||
|
||||
.PHONY: js-lint
|
||||
js-lint: ## Run javascript linting scanners
|
||||
@@ -68,25 +93,37 @@ js-test: ## Run javascript unit tests
|
||||
|
||||
.PHONY: fix-imports
|
||||
fix-imports: ## Fix imports using isort
|
||||
pipenv run isort ./app ./tests
|
||||
poetry run isort ./app ./tests
|
||||
|
||||
.PHONY: py-lock
|
||||
py-lock: ## Syncs dependencies and updates lock file without performing recursive internal updates
|
||||
poetry lock --no-update
|
||||
poetry install --sync
|
||||
|
||||
.PHONY: update-utils
|
||||
update-utils: ## Forces Poetry to pull the latest changes from the notifications-utils repo; requires that you commit the changes to poetry.lock!
|
||||
poetry update notifications-utils
|
||||
@echo
|
||||
@echo !!! PLEASE MAKE SURE TO COMMIT AND PUSH THE UPDATED poetry.lock FILE !!!
|
||||
@echo
|
||||
|
||||
.PHONY: freeze-requirements
|
||||
freeze-requirements: ## create static requirements.txt
|
||||
pipenv requirements > requirements.txt
|
||||
poetry export --without-hashes --format=requirements.txt > requirements.txt
|
||||
|
||||
.PHONY: pip-audit
|
||||
pip-audit:
|
||||
pipenv requirements > requirements.txt
|
||||
pipenv requirements --dev > requirements_for_test.txt
|
||||
pipenv run pip-audit -r requirements.txt
|
||||
-pipenv run pip-audit -r requirements_for_test.txt
|
||||
poetry requirements > requirements.txt
|
||||
poetry requirements --dev > requirements_for_test.txt
|
||||
poetry run pip-audit -r requirements.txt
|
||||
poetry run pip-audit -r requirements_for_test.txt
|
||||
|
||||
.PHONY: audit
|
||||
audit: npm-audit pip-audit
|
||||
|
||||
.PHONY: static-scan
|
||||
static-scan:
|
||||
pipenv run bandit -r app/
|
||||
poetry run bandit -r app/
|
||||
|
||||
.PHONY: a11y-scan
|
||||
a11y-scan:
|
||||
|
||||
59
Pipfile
@@ -1,59 +0,0 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
ago = "~=0.0.95"
|
||||
blinker = "~=1.4"
|
||||
fido2 = "~=0.9"
|
||||
flask = "~=2.3"
|
||||
flask-basicauth = "~=0.2"
|
||||
flask-login = "~=0.6"
|
||||
flask-wtf = "~=1.1"
|
||||
gds-metrics = {version = "==0.2.4", ref = "6f1840a57b6fb1ee40b7e84f2f18ec229de8aa72", git = "https://github.com/alphagov/gds_metrics_python.git"}
|
||||
govuk-bank-holidays = "==0.13"
|
||||
govuk-frontend-jinja = {version = "==0.5.8-alpha", git = "https://github.com/alphagov/govuk-frontend-jinja.git"}
|
||||
gunicorn = {version = "==20.1.0", extras = ["eventlet"], ref = "1299ea9e967a61ae2edebe191082fd169b864c64", git = "https://github.com/benoitc/gunicorn.git"}
|
||||
humanize = "~=4.1"
|
||||
itsdangerous = "~=2.1"
|
||||
jinja2 = "~=3.1"
|
||||
notifications-python-client = "==8.0.1"
|
||||
prometheus-client = "==0.14.1"
|
||||
pyexcel = "==0.7.0"
|
||||
pyexcel-io = "==0.6.6"
|
||||
pyexcel-ods3 = "==0.6.1"
|
||||
pyexcel-xls = "==0.7.0"
|
||||
pyexcel-xlsx = "==0.6.0"
|
||||
openpyxl = "==3.0.10"
|
||||
pyproj = "==3.6.0"
|
||||
python-dotenv = "==1.0.0"
|
||||
pytz = "==2023.3"
|
||||
rtreelib = "==0.2.0"
|
||||
werkzeug = "~=2.3"
|
||||
wtforms = "~=3.0"
|
||||
newrelic = "*"
|
||||
flask-talisman = "*"
|
||||
notifications-utils = {editable = true, ref = "main", git = "https://github.com/GSA/notifications-utils.git"}
|
||||
coverage = "*"
|
||||
|
||||
[dev-packages]
|
||||
isort = "==5.12.0"
|
||||
pytest = "==7.4.0"
|
||||
pytest-env = "==0.6.2"
|
||||
pytest-mock = "==3.11.1"
|
||||
pytest-xdist = "==2.5.0"
|
||||
beautifulsoup4 = "==4.12.2"
|
||||
freezegun = "==1.2.2"
|
||||
flake8 = "==4.0.1"
|
||||
flake8-bugbear = "==22.4.25"
|
||||
flake8-print = "==5.0.0"
|
||||
moto = "~=4.1"
|
||||
requests-mock = "==1.11.0"
|
||||
# used for creating manifest file locally
|
||||
jinja2-cli = {version = "==0.8.2", extras = ["yaml"]}
|
||||
pip-audit = "*"
|
||||
bandit = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.9"
|
||||
2109
Pipfile.lock
generated
480
README.md
@@ -1,6 +1,8 @@
|
||||
# Notify UI
|
||||
# Notify.gov Admin UI
|
||||
|
||||
This is the Notify front-end for government users and admins. To see it in action, check out [the demo site](https://notify-demo.app.cloud.gov) (contact team for credentials).
|
||||
This is the Notify front-end for government users and admins. To see it in
|
||||
action, check out [the demo site](https://notify-demo.app.cloud.gov)
|
||||
(contact team for credentials).
|
||||
|
||||
Through the interface, users can:
|
||||
|
||||
@@ -9,50 +11,433 @@ Through the interface, users can:
|
||||
- Send batch SMS by uploading a CSV
|
||||
- View their history of notifications
|
||||
|
||||
The [Notify API](https://github.com/GSA/notifications-api) provides the UI's backend and is required for most things to function. Set that up first!
|
||||
The [Notify.gov API](https://github.com/GSA/notifications-api) provides the
|
||||
UI's backend and is required for most things to function. Set that up first!
|
||||
|
||||
## Local setup
|
||||
Our other repositories are:
|
||||
|
||||
### Common steps
|
||||
- [notifications-admin](https://github.com/GSA/notifications-admin)
|
||||
- [notifications-utils](https://github.com/GSA/notifications-utils)
|
||||
- [us-notify-compliance](https://github.com/GSA/us-notify-compliance/)
|
||||
- [notify-python-demo](https://github.com/GSA/notify-python-demo)
|
||||
|
||||
1. Install pre-requisites for setup:
|
||||
- [jq](https://stedolan.github.io/jq/): `brew install jq`
|
||||
- [terraform](https://www.terraform.io/): `brew install terraform` or `brew install tfenv` and use `tfenv` to install `terraform ~> 1.4.0`
|
||||
- [cf-cli@8](https://docs.cloudfoundry.org/cf-cli/install-go-cli.html): `brew install cloudfoundry/tap/cf-cli@8`
|
||||
1. [Log into cloud.gov](https://cloud.gov/docs/getting-started/setup/#set-up-the-command-line): `cf login -a api.fr.cloud.gov --sso`
|
||||
1. Ensure you have access to the `notify-local-dev` and `notify-staging` spaces in cloud.gov
|
||||
1. Run the API setup steps
|
||||
1. Run the development terraform with:
|
||||
## Before You Start
|
||||
|
||||
```
|
||||
$ cd terraform/development
|
||||
$ ./run.sh
|
||||
```
|
||||
You will need the following items:
|
||||
|
||||
1. If you want to send data to New Relic from your local develpment environment, set `NEW_RELIC_LICENSE_KEY` within `.env`
|
||||
1. Follow the instructions for either `Direct installation` or `Docker installation` below
|
||||
- An active cloud.gov account with the correct permissions - speak with your
|
||||
onboarding buddy for help with
|
||||
[setting up an account](https://cloud.gov/sign-up/) (requires a `.mil`,
|
||||
`.gov`, or `.fed.us` email address) and getting access to the
|
||||
`notify-local-dev` and `notify-staging` spaces.
|
||||
- Admin priviliges and SSH access on your machine; you may need to work with
|
||||
your organization's IT support staff if you're not sure or don't currently
|
||||
have this access.
|
||||
|
||||
### Direct installation
|
||||
**In addition, you should set up the Notify.gov API first!**
|
||||
|
||||
1. Get the API running
|
||||
[Follow the instructions here to set up the Notify.gov API.](https://github.com/GSA/notifications-api#before-you-start)
|
||||
|
||||
1. Install [pipenv](https://pipenv.pypa.io/en/latest/)
|
||||
The Notify.gov API is required in order for the Notify.gov Admin UI to run, and
|
||||
it will also take care of many of the steps that are listed here. The sections
|
||||
that are a repeat from the API setup are flagged with an **[API Step]** label
|
||||
in front of them.
|
||||
|
||||
1. Install [nvm (Node Version Manager)](https://github.com/nvm-sh/nvm#installing-and-updating)
|
||||
## Local Environment Setup
|
||||
|
||||
1. Install Python and Node dependencies
|
||||
This project is set up to work with
|
||||
[nvm (Node Version Manager)](https://github.com/nvm-sh/nvm#installing-and-updating)
|
||||
for managing and using Node.js (version 16.15.1) and the `npm` package manager.
|
||||
|
||||
`make bootstrap`
|
||||
|
||||
1. Run the Flask server
|
||||
|
||||
`make run-flask`
|
||||
|
||||
1. Go to http://localhost:6012
|
||||
These instructions will walk you through how to set your machine up with all of
|
||||
the required tools for this project.
|
||||
|
||||
### Docker installation
|
||||
|
||||
If you are using VS Code, there are also instructions for [running inside Docker](./docs/docker-remote-containers.md)
|
||||
If you are using VS Code, there are also instructions for
|
||||
[running inside Docker](./docs/docker-remote-containers.md).
|
||||
|
||||
### [API Step] Project Pre-Requisite Setup
|
||||
|
||||
On MacOS, using [Homebrew](https://brew.sh/) for package management is highly
|
||||
recommended. This helps avoid some known installation issues. Start by following
|
||||
the installation instructions on the Homebrew homepage.
|
||||
|
||||
**Note:** You will also need Xcode or the Xcode Command Line Tools installed. The
|
||||
quickest way to do this is is by installing the command line tools in the shell:
|
||||
|
||||
```sh
|
||||
xcode-select –-install
|
||||
```
|
||||
|
||||
#### [API Step] Homebrew Setup
|
||||
|
||||
If this is your first time installing Homebrew on your machine, you may need to
|
||||
add its binaries to your system's `$PATH` environment variable so that you can
|
||||
use the `brew` command. Try running `brew help` to see if Homebrew is
|
||||
recognized and runs properly. If that fails, then you'll need to add a
|
||||
configuration line to wherever your `$PATH` environment variable is set.
|
||||
|
||||
Your system `$PATH` environment variable is likely set in one of these
|
||||
locations:
|
||||
|
||||
For BASH shells:
|
||||
- `~/.bashrc`
|
||||
- `~/.bash_profile`
|
||||
- `~/.profile`
|
||||
|
||||
For ZSH shells:
|
||||
- `~/.zshrc`
|
||||
- `~/.zprofile`
|
||||
|
||||
There may be different files that you need to modify for other shell
|
||||
environments.
|
||||
|
||||
Which file you need to modify depends on whether or not you are running an
|
||||
interactive shell or a login shell
|
||||
(see [this Stack Overflow post](https://stackoverflow.com/questions/18186929/what-are-the-differences-between-a-login-shell-and-interactive-shell)
|
||||
for an explanation of the differences). If you're still not sure, please ask
|
||||
the team for help!
|
||||
|
||||
Once you determine which file you'll need to modify, add these lines before any
|
||||
lines that add or modify the `$PATH` environment variable; near or at the top
|
||||
of the file is appropriate:
|
||||
|
||||
```sh
|
||||
# Homebrew setup
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
```
|
||||
|
||||
This will make sure Homebrew gets setup correctly. Once you make these changes,
|
||||
either start a new shell session or source the file
|
||||
(`source ~/.FILE-YOU-MODIFIED`) you modified to have your system recognize the
|
||||
changes.
|
||||
|
||||
Verify that Homebrew is now working by trying to run `brew help` again.
|
||||
|
||||
### [API Step] System-Level Package Installation
|
||||
|
||||
There are several packages you will need to install for your system in order to
|
||||
get the app running (and these are good to have in general for any software
|
||||
development).
|
||||
|
||||
Start off with these packages since they're quick and don't require additional
|
||||
configuration after installation to get working out of the box:
|
||||
|
||||
- [jq](https://stedolan.github.io/jq/) - for working with JSON in the command
|
||||
line
|
||||
- [git](https://git-scm.com/) - for version control management
|
||||
- [tfenv](https://github.com/tfutils/tfenv) - for managing
|
||||
[Terraform](https://www.terraform.io/) installations
|
||||
- [cf-cli@8](https://docs.cloudfoundry.org/cf-cli/install-go-cli.html) - for
|
||||
working with a Cloud Foundry platform (e.g., cloud.gov)
|
||||
- [vim](https://www.vim.org/) - for editing files more easily in the command
|
||||
line
|
||||
- [wget](https://www.gnu.org/software/wget/) - for retrieving files in the
|
||||
command line
|
||||
|
||||
You can install them by running the following:
|
||||
|
||||
```sh
|
||||
brew install jq git tfenv cloudfoundry/tap/cf-cli@8 vim wget
|
||||
```
|
||||
|
||||
#### [API Step] Terraform Installation
|
||||
|
||||
As a part of the installation above, you just installed `tfenv` to manage
|
||||
Terraform installations. This is great, but you still need to install Terraform
|
||||
itself, which can be done with this command:
|
||||
|
||||
```sh
|
||||
tfenv install latest:^1.4.0
|
||||
```
|
||||
|
||||
_NOTE: This project currently uses the latest `1.4.x release of Terraform._
|
||||
|
||||
#### [API Step] Python Installation
|
||||
|
||||
Now we're going to install a tool to help us manage Python versions and
|
||||
virtual environments on our system. First, we'll install
|
||||
[pyenv](https://github.com/pyenv/pyenv) and one of its plugins,
|
||||
[pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv), with Homebrew:
|
||||
|
||||
```sh
|
||||
brew install pyenv pyenv-virtualenv
|
||||
```
|
||||
|
||||
When these finish installing, you'll need to make another adjustment in the
|
||||
file that you adjusted for your `$PATH` environment variable and Homebrew's
|
||||
setup. Open the file, and add these lines to it:
|
||||
|
||||
```
|
||||
# pyenv setup
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init -)"
|
||||
eval "$(pyenv virtualenv-init -)"
|
||||
```
|
||||
|
||||
Once again, start a new shell session or source the file in your current shell
|
||||
session to make the changes take effect.
|
||||
|
||||
Now we're ready to install the Python version we need with `pyenv`, like so:
|
||||
|
||||
```sh
|
||||
pyenv install 3.9
|
||||
```
|
||||
|
||||
This will install the latest version of Python 3.9.
|
||||
|
||||
_NOTE: This project currently runs on Python 3.9.x._
|
||||
|
||||
#### [API Step] Python Dependency Installation
|
||||
|
||||
Lastly, we need to install the tool we use to manage Python dependencies within
|
||||
the project, which is [poetry](https://python-poetry.org/).
|
||||
|
||||
Visit the
|
||||
[official installer instructions page](https://python-poetry.org/docs/#installing-with-the-official-installer)
|
||||
and follow the steps to install Poetry directly with the script.
|
||||
|
||||
This will ensure `poetry` doesn't conflict with any project virtual environments
|
||||
and can update itself properly.
|
||||
|
||||
#### Node.js and npm Installation
|
||||
|
||||
The project will manage most of the Node.js pieces for you, but you will need to
|
||||
install
|
||||
[nvm (Node Version Manager)](https://github.com/nvm-sh/nvm#installing-and-updating)
|
||||
in order for things to work.
|
||||
|
||||
Follow the steps in the installation instructions to get `nvm` installed with
|
||||
the install script they provide, and double check that the file that you
|
||||
adjusted for your `$PATH` environment variable and Homebrew's setup is properly
|
||||
updated with the `nvm` setup as well.
|
||||
|
||||
Open the file and check for the `nvm` setup lines; if they're not there, then
|
||||
add the lines found in the
|
||||
[nvm installation instructions](https://github.com/nvm-sh/nvm#install--update-script)
|
||||
to to the file after the `pyenv` section you created, e.g.:
|
||||
|
||||
```
|
||||
# nvm setup
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||||
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
|
||||
```
|
||||
|
||||
### First-Time Project Setup
|
||||
|
||||
Once all of pre-requisites for the project are installed and you have a
|
||||
cloud.gov account, you can now set up the admin project and get things running
|
||||
locally!
|
||||
|
||||
First, clone the respository in the directory of your choosing on your machine:
|
||||
|
||||
```sh
|
||||
git clone git@github.com:GSA/notifications-admin.git
|
||||
```
|
||||
|
||||
Now go into the project directory (`notifications-admin` by default), create a
|
||||
virtual environment, and set the local Python version to point to the virtual
|
||||
environment (assumes version Python `3.9.18` is what is installed on your
|
||||
machine):
|
||||
|
||||
```sh
|
||||
cd notifications-admin
|
||||
pyenv virtualenv 3.9.18 notify-admin
|
||||
pyenv local notify-admin
|
||||
```
|
||||
|
||||
_If you're not sure which version of Python was installed with `pyenv`, you can check by running `pyenv versions` and it'll list everything available currently._
|
||||
|
||||
Now [log into cloud.gov](https://cloud.gov/docs/getting-started/setup/#set-up-the-command-line)
|
||||
in the command line by using this command:
|
||||
|
||||
```sh
|
||||
cf login -a api.fr.cloud.gov --sso
|
||||
```
|
||||
|
||||
_REMINDER: Ensure you have access to the `notify-local-dev` and `notify-staging` spaces in cloud.gov_
|
||||
|
||||
Now run the development Terraform setup by navigating to the development
|
||||
folder and running the script in it:
|
||||
|
||||
```sh
|
||||
cd terraform/development
|
||||
./run.sh
|
||||
```
|
||||
|
||||
In addition to some infrastructure setup, this will also create a local `.env`
|
||||
file for you in the project's root directory, which will include a handful of
|
||||
project-specific environment variables.
|
||||
|
||||
#### Updating the .env file for E2E tests
|
||||
|
||||
With the newly created `.env` file in place, you'll need to make one more
|
||||
adjustment in it for things to run properly for the E2E tests.
|
||||
|
||||
Open the `.env` file and look for the `NODE_EXTRA_CA_CERTS` environment variable
|
||||
toward the top. You will need to modify its value to be the path to Homebrew's
|
||||
CA certificate file.
|
||||
|
||||
Run `brew --prefix` to see Homebrew's root directory, and use that value to
|
||||
update the `NODE_EXTRA_CA_CERTS` environment variable with the path. For
|
||||
example, if `brew --prefix` gave `/opt/homebrew` as output, then the line would
|
||||
look like this:
|
||||
|
||||
```
|
||||
NODE_EXTRA_CA_CERTS=/opt/homebrew/etc/ca-certificates/cert.pem
|
||||
```
|
||||
|
||||
Save this change to the `.env` file and you'll be squared away.
|
||||
|
||||
## Running the Project and Routine Maintenance
|
||||
|
||||
The first time you run the project you'll need to run the project setup from the
|
||||
root project directory:
|
||||
|
||||
```sh
|
||||
make bootstrap
|
||||
```
|
||||
|
||||
This command is handled by the `Makefile` file in the root project directory, as
|
||||
are a few others.
|
||||
|
||||
_NOTE: You'll want to occasionally run `make bootstrap` to keep your project up-to-date, especially when there are dependency updates._
|
||||
|
||||
Now you can run the web server and background workers for asynchronous jobs:
|
||||
|
||||
```sh
|
||||
make run-flask
|
||||
```
|
||||
|
||||
This will run the local development web server and make the admin site
|
||||
available at http://localhost:6012; remember to make sure that the Notify.gov
|
||||
API is running as well!
|
||||
|
||||
## Git Hooks
|
||||
|
||||
We're using [`pre-commit`](https://pre-commit.com/) to manage hooks in order to
|
||||
automate common tasks or easily-missed cleanup. It's installed as part of
|
||||
`make bootstrap` and is limited to this project's virtualenv.
|
||||
|
||||
To run the hooks in advance of a `git` operation, use
|
||||
`poetry run pre-commit run`. For running across the whole codebase (useful after
|
||||
adding a new hook), use `poetry run pre-commit run --all-files`.
|
||||
|
||||
The configuration is stored in `.pre-commit-config.yaml`. In that config, there
|
||||
are links to the repos from which the hooks are pulled, so hop through there if
|
||||
ou want a detailed description of what each one is doing.
|
||||
|
||||
We do not maintain any hooks in this repository.
|
||||
|
||||
## Python Dependency Management
|
||||
|
||||
We're using [`Poetry`](https://python-poetry.org/) for managing our Python
|
||||
dependencies and local virtual environments. When it comes to managing the
|
||||
Python dependencies, there are a couple of things to bear in mind.
|
||||
|
||||
For situations where you manually manipulate the `pyproject.toml` file, you
|
||||
should use the `make py-lock` command to sync the `poetry.lock` file. This will
|
||||
ensure that you don't inadvertently bring in other transitive dependency updates
|
||||
that have not been fully tested with the project yet.
|
||||
|
||||
If you're just trying to update a dependency to a newer (or the latest) version,
|
||||
you should let Poetry take care of that for you by running the following:
|
||||
|
||||
```sh
|
||||
poetry update <dependency> [<dependency>...]
|
||||
```
|
||||
|
||||
You can specify more than one dependency together. With this command, Poetry
|
||||
will do the following for you:
|
||||
|
||||
- Find the latest compatible version(s) of the specified dependency/dependencies
|
||||
- Install the new versions
|
||||
- Update and sync the `poetry.lock` file
|
||||
|
||||
In either situation, once you are finished and have verified the dependency
|
||||
changes are working, please be sure to commit both the `pyproject.toml` and
|
||||
`poetry.lock` files.
|
||||
|
||||
### Keeping the notification-utils Dependency Up-to-Date
|
||||
|
||||
The `notifications-utils` dependency references the other repository we have at
|
||||
https://github.com/GSA/notifications-utils - this dependency requires a bit of
|
||||
extra legwork to ensure it stays up-to-date.
|
||||
|
||||
Whenever a PR is merged in the `notifications-utils` repository, we need to make
|
||||
sure the changes are pulled in here and committed to this repository as well.
|
||||
You can do this by going through these steps:
|
||||
|
||||
- Make sure your local `main` branch is up-to-date
|
||||
- Create a new branch to work in
|
||||
- Run `make update-utils`
|
||||
- Commit the updated `poetry.lock` file and push the changes
|
||||
- Make a new PR with the change
|
||||
- Have the PR get reviewed and merged
|
||||
|
||||
### Python dependency management
|
||||
|
||||
We're using [`Poetry`](https://python-poetry.org/) for managing our Python
|
||||
dependencies and local virtual environments. When it comes to managing the
|
||||
Python dependencies, there are a couple of things to bear in mind.
|
||||
|
||||
For situations where you manually manipulate the `pyproject.toml` file, you
|
||||
should use the `make py-lock` command to sync the `poetry.lock` file. This will
|
||||
ensure that you don't inadvertently bring in other transitive dependency updates
|
||||
that have not been fully tested with the project yet.
|
||||
|
||||
If you're just trying to update a dependency to a newer (or the latest) version,
|
||||
you should let Poetry take care of that for you by running the following:
|
||||
|
||||
```sh
|
||||
poetry update <dependency> [<dependency>...]
|
||||
```
|
||||
|
||||
You can specify more than one dependency together. With this command, Poetry
|
||||
will do the following for you:
|
||||
|
||||
- Find the latest compatible version(s) of the specified dependency/dependencies
|
||||
- Install the new versions
|
||||
- Update and sync the `poetry.lock` file
|
||||
|
||||
In either situation, once you are finished and have verified the dependency
|
||||
changes are working, please be sure to commit both the `pyproject.toml` and
|
||||
`poetry.lock` files.
|
||||
|
||||
### Keeping the notification-utils dependency up-to-date
|
||||
|
||||
The `notifications-utils` dependency references the other repository we have at
|
||||
https://github.com/GSA/notifications-utils - this dependency requires a bit of
|
||||
extra legwork to ensure it stays up-to-date.
|
||||
|
||||
Whenever a PR is merged in the `notifications-utils` repository, we need to make
|
||||
sure the changes are pulled in here and committed to this repository as well.
|
||||
You can do this by going through these steps:
|
||||
|
||||
- Make sure your local `main` branch is up-to-date
|
||||
- Create a new branch to work in
|
||||
- Run `make update-utils`
|
||||
- Commit the updated `poetry.lock` file and push the changes
|
||||
- Make a new PR with the change
|
||||
- Have the PR get reviewed and merged
|
||||
|
||||
## Known Installation Issues
|
||||
|
||||
### Python Installation Errors
|
||||
|
||||
On M1 Macs, if you get a `fatal error: 'Python.h' file not found` message, try a
|
||||
different method of installing Python. The recommended approach is to use
|
||||
[`pyenv`](https://github.com/pyenv/pyenv), as noted above in the installation
|
||||
instructions.
|
||||
|
||||
If you're using PyCharm for Python development, we've noticed some quirkiness
|
||||
with the IDE and the interaction between Poetry and virtual environment
|
||||
management that could cause a variety of problems to come up during project
|
||||
setup and dependency management. Other tools, such as Visual Studio Code, have
|
||||
proven to be a smoother experience for folks.
|
||||
|
||||
## To test the application
|
||||
|
||||
@@ -66,11 +451,13 @@ make test
|
||||
npm run test-watch
|
||||
```
|
||||
|
||||
To run a specific JavaScript test, you'll need to copy the full command from `package.json`.
|
||||
To run a specific JavaScript test, you'll need to copy the full command from
|
||||
`package.json`.
|
||||
|
||||
## Running a11y-scans locally
|
||||
|
||||
Unlike most of the tests and scans, pa11y-ci cannot currently be run from within the VSCode dev container.
|
||||
Unlike most of the tests and scans, pa11y-ci cannot currently be run from within
|
||||
the VSCode dev container.
|
||||
|
||||
1. `make run-flask` from within the devcontainer
|
||||
2. Run `make a11y-scan` from your host computer.
|
||||
@@ -83,16 +470,31 @@ Unlike most of the tests and scans, pa11y-ci cannot currently be run from within
|
||||
|
||||
## License && public domain
|
||||
|
||||
Work through [commit `543be77`](https://github.com/GSA/notifications-admin/commit/543be77776b64fddb6ba70fbb015ecd81a372478) is licensed by the UK government under the MIT license. Work after that commit is in the worldwide public domain. See [LICENSE.md](./LICENSE.md) for more information.
|
||||
Work through
|
||||
[commit `543be77`](https://github.com/GSA/notifications-admin/commit/543be77776b64fddb6ba70fbb015ecd81a372478)
|
||||
is licensed by the UK government under the MIT license. Work after that commit
|
||||
is in the worldwide public domain. See [LICENSE.md](./LICENSE.md) for more
|
||||
information.
|
||||
|
||||
## Contributing
|
||||
|
||||
As stated in [CONTRIBUTING.md](CONTRIBUTING.md), all contributions to this project will be released under the CC0 dedication. By submitting a pull request, you are agreeing to comply with this waiver of copyright interest.
|
||||
As stated in [CONTRIBUTING.md](CONTRIBUTING.md), all contributions to this
|
||||
project will be released under the CC0 dedication. By submitting a pull request,
|
||||
you are agreeing to comply with this waiver of copyright interest.
|
||||
|
||||
## About the TTS Public Benefits Studio
|
||||
|
||||
The Public Benefits Studio is a team inside of [GSA’s Technology Transformation Services](https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services) (TTS), home to innovative programs like [18F](https://18f.gsa.gov/) and [Login.gov](https://login.gov). We collaborate with benefits programs to develop shared technology tools and best practices that reduce the burden of navigating government programs for low income individuals and families.
|
||||
The Public Benefits Studio is a team inside of
|
||||
[GSA’s Technology Transformation Services](https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services)
|
||||
(TTS), home to innovative programs like [18F](https://18f.gsa.gov/) and
|
||||
[Login.gov](https://login.gov). We collaborate with benefits programs to develop
|
||||
shared technology tools and best practices that reduce the burden of navigating
|
||||
government programs for low income individuals and families.
|
||||
|
||||
We’re a cross-functional team of technologists with specialized experience working across public benefits programs like Medicaid, SNAP, and unemployment insurance.
|
||||
We’re a cross-functional team of technologists with specialized experience
|
||||
working across public benefits programs like Medicaid, SNAP, and unemployment
|
||||
insurance.
|
||||
|
||||
For more information on what we're working on, the Notify tool, and how to get involved with our team, [see our flyer.](https://github.com/GSA/notifications-admin/blob/main/docs/notify-pilot-flyer.md)
|
||||
For more information on what we're working on, the Notify tool, and how to get
|
||||
involved with our team,
|
||||
[see our flyer.](https://github.com/GSA/notifications-admin/blob/main/docs/notify-pilot-flyer.md)
|
||||
|
||||
273
app/__init__.py
@@ -2,6 +2,7 @@ import os
|
||||
import pathlib
|
||||
from functools import partial
|
||||
from time import monotonic
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
import jinja2
|
||||
from flask import (
|
||||
@@ -20,7 +21,6 @@ from flask_login import LoginManager, current_user
|
||||
from flask_talisman import Talisman
|
||||
from flask_wtf import CSRFProtect
|
||||
from flask_wtf.csrf import CSRFError
|
||||
from gds_metrics import GDSMetrics
|
||||
from govuk_frontend_jinja.flask_ext import init_govuk_frontend
|
||||
from itsdangerous import BadSignature
|
||||
from notifications_python_client.errors import HTTPError
|
||||
@@ -34,10 +34,9 @@ from werkzeug.exceptions import HTTPException as WerkzeugHTTPException
|
||||
from werkzeug.exceptions import abort
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from app import proxy_fix, webauthn_server
|
||||
from app import proxy_fix
|
||||
from app.asset_fingerprinter import asset_fingerprinter
|
||||
from app.config import configs
|
||||
from app.custom_auth import CustomBasicAuth
|
||||
from app.extensions import redis_client, zendesk_client
|
||||
from app.formatters import (
|
||||
convert_to_boolean,
|
||||
@@ -105,18 +104,11 @@ from app.notify_client.organizations_api_client import organizations_client
|
||||
from app.notify_client.performance_dashboard_api_client import (
|
||||
performance_dashboard_api_client,
|
||||
)
|
||||
from app.notify_client.platform_stats_api_client import (
|
||||
platform_stats_api_client,
|
||||
)
|
||||
from app.notify_client.provider_client import provider_client
|
||||
from app.notify_client.platform_stats_api_client import platform_stats_api_client
|
||||
from app.notify_client.service_api_client import service_api_client
|
||||
from app.notify_client.status_api_client import status_api_client
|
||||
from app.notify_client.template_folder_api_client import (
|
||||
template_folder_api_client,
|
||||
)
|
||||
from app.notify_client.template_statistics_api_client import (
|
||||
template_statistics_client,
|
||||
)
|
||||
from app.notify_client.template_folder_api_client import template_folder_api_client
|
||||
from app.notify_client.template_statistics_api_client import template_statistics_client
|
||||
from app.notify_client.upload_api_client import upload_api_client
|
||||
from app.notify_client.user_api_client import user_api_client
|
||||
from app.url_converters import (
|
||||
@@ -128,32 +120,27 @@ from app.url_converters import (
|
||||
login_manager = LoginManager()
|
||||
csrf = CSRFProtect()
|
||||
talisman = Talisman()
|
||||
metrics = GDSMetrics()
|
||||
basic_auth = CustomBasicAuth()
|
||||
|
||||
|
||||
# The current service attached to the request stack.
|
||||
current_service = LocalProxy(partial(getattr, request_ctx, 'service'))
|
||||
current_service = LocalProxy(partial(getattr, request_ctx, "service"))
|
||||
|
||||
# The current organization attached to the request stack.
|
||||
current_organization = LocalProxy(partial(getattr, request_ctx, 'organization'))
|
||||
current_organization = LocalProxy(partial(getattr, request_ctx, "organization"))
|
||||
|
||||
navigation = {
|
||||
'casework_navigation': CaseworkNavigation(),
|
||||
'main_navigation': MainNavigation(),
|
||||
'header_navigation': HeaderNavigation(),
|
||||
'org_navigation': OrgNavigation(),
|
||||
"casework_navigation": CaseworkNavigation(),
|
||||
"main_navigation": MainNavigation(),
|
||||
"header_navigation": HeaderNavigation(),
|
||||
"org_navigation": OrgNavigation(),
|
||||
}
|
||||
|
||||
|
||||
def _csp(config):
|
||||
asset_domain = config['ASSET_DOMAIN']
|
||||
logo_domain = config['LOGO_CDN_DOMAIN']
|
||||
asset_domain = config["ASSET_DOMAIN"]
|
||||
logo_domain = config["LOGO_CDN_DOMAIN"]
|
||||
return {
|
||||
"default-src": [
|
||||
"'self'",
|
||||
asset_domain
|
||||
],
|
||||
"default-src": ["'self'", asset_domain],
|
||||
"frame-ancestors": "'none'",
|
||||
"form-action": "'self'",
|
||||
"script-src": [
|
||||
@@ -162,48 +149,39 @@ def _csp(config):
|
||||
"'unsafe-eval'",
|
||||
"https://js-agent.newrelic.com",
|
||||
"https://gov-bam.nr-data.net",
|
||||
"https://www.googletagmanager.com",
|
||||
"https://www.google-analytics.com",
|
||||
"https://dap.digitalgov.gov",
|
||||
],
|
||||
"connect-src": [
|
||||
"'self'",
|
||||
"https://gov-bam.nr-data.net"
|
||||
"https://gov-bam.nr-data.net",
|
||||
"https://www.google-analytics.com",
|
||||
],
|
||||
"style-src": [
|
||||
"'self'",
|
||||
asset_domain
|
||||
],
|
||||
"img-src": [
|
||||
"'self'",
|
||||
asset_domain,
|
||||
logo_domain
|
||||
]
|
||||
"style-src": ["'self'", asset_domain],
|
||||
"img-src": ["'self'", asset_domain, logo_domain],
|
||||
}
|
||||
|
||||
|
||||
def create_app(application):
|
||||
notify_environment = os.environ['NOTIFY_ENVIRONMENT']
|
||||
notify_environment = os.environ["NOTIFY_ENVIRONMENT"]
|
||||
|
||||
application.config.from_object(configs[notify_environment])
|
||||
asset_fingerprinter._asset_root = application.config['ASSET_PATH']
|
||||
asset_fingerprinter._asset_root = application.config["ASSET_PATH"]
|
||||
|
||||
init_app(application)
|
||||
|
||||
if 'extensions' not in application.jinja_options:
|
||||
application.jinja_options['extensions'] = []
|
||||
if "extensions" not in application.jinja_options:
|
||||
application.jinja_options["extensions"] = []
|
||||
|
||||
init_govuk_frontend(application)
|
||||
init_jinja(application)
|
||||
|
||||
for client in (
|
||||
# Gubbins
|
||||
# Note, metrics purposefully first so we start measuring response times as early as possible before any
|
||||
# other `app.before_request` handlers (introduced by any of these clients) are processed (which would
|
||||
# otherwise mean we aren't measuring the full response time)
|
||||
metrics,
|
||||
csrf,
|
||||
login_manager,
|
||||
proxy_fix,
|
||||
request_helper,
|
||||
|
||||
# API clients
|
||||
api_key_api_client,
|
||||
billing_api_client,
|
||||
@@ -218,54 +196,48 @@ def create_app(application):
|
||||
organizations_client,
|
||||
performance_dashboard_api_client,
|
||||
platform_stats_api_client,
|
||||
provider_client,
|
||||
service_api_client,
|
||||
status_api_client,
|
||||
template_folder_api_client,
|
||||
template_statistics_client,
|
||||
upload_api_client,
|
||||
user_api_client,
|
||||
|
||||
# External API clients
|
||||
redis_client,
|
||||
zendesk_client,
|
||||
|
||||
):
|
||||
client.init_app(application)
|
||||
|
||||
talisman.init_app(
|
||||
application,
|
||||
content_security_policy=_csp(application.config),
|
||||
content_security_policy_nonce_in=['style-src', 'script-src'],
|
||||
content_security_policy_nonce_in=["style-src", "script-src"],
|
||||
permissions_policy={
|
||||
'accelerometer': '()',
|
||||
'ambient-light-sensor': '()',
|
||||
'autoplay': '()',
|
||||
'battery': '()',
|
||||
'camera': '()',
|
||||
'document-domain': '()',
|
||||
'geolocation': '()',
|
||||
'gyroscope': '()',
|
||||
'local-fonts': '()',
|
||||
'magnetometer': '()',
|
||||
'microphone': '()',
|
||||
'midi': '()',
|
||||
'payment': '()',
|
||||
'screen-wake-lock': '()'
|
||||
"accelerometer": "()",
|
||||
"ambient-light-sensor": "()",
|
||||
"autoplay": "()",
|
||||
"battery": "()",
|
||||
"camera": "()",
|
||||
"document-domain": "()",
|
||||
"geolocation": "()",
|
||||
"gyroscope": "()",
|
||||
"local-fonts": "()",
|
||||
"magnetometer": "()",
|
||||
"microphone": "()",
|
||||
"midi": "()",
|
||||
"payment": "()",
|
||||
"screen-wake-lock": "()",
|
||||
},
|
||||
frame_options='deny',
|
||||
force_https=(application.config['HTTP_PROTOCOL'] == 'https')
|
||||
frame_options="deny",
|
||||
force_https=(application.config["HTTP_PROTOCOL"] == "https"),
|
||||
)
|
||||
logging.init_app(application)
|
||||
webauthn_server.init_app(application)
|
||||
|
||||
login_manager.login_view = 'main.sign_in'
|
||||
login_manager.login_message_category = 'default'
|
||||
login_manager.login_view = "main.sign_in"
|
||||
login_manager.login_message_category = "default"
|
||||
login_manager.session_protection = None
|
||||
login_manager.anonymous_user = AnonymousUser
|
||||
|
||||
setup_basic_auth(application)
|
||||
|
||||
# make sure we handle unicode correctly
|
||||
redis_client.redis_store.decode_responses = True
|
||||
|
||||
@@ -284,28 +256,32 @@ def create_app(application):
|
||||
|
||||
|
||||
def init_app(application):
|
||||
application.before_request(redirect_notify_to_beta)
|
||||
application.before_request(load_service_before_request)
|
||||
application.before_request(load_organization_before_request)
|
||||
application.before_request(request_helper.check_proxy_header_before_request)
|
||||
application.before_request(make_session_permanent)
|
||||
application.after_request(save_service_or_org_after_request)
|
||||
|
||||
start = len(asset_fingerprinter._filesystem_path)
|
||||
font_paths = [
|
||||
str(item)[len(asset_fingerprinter._filesystem_path):]
|
||||
for item in pathlib.Path(asset_fingerprinter._filesystem_path).glob('fonts/*.woff2')
|
||||
str(item)[start:]
|
||||
for item in pathlib.Path(asset_fingerprinter._filesystem_path).glob(
|
||||
"fonts/*.woff2"
|
||||
)
|
||||
]
|
||||
|
||||
@application.context_processor
|
||||
def _attach_current_service():
|
||||
return {'current_service': current_service}
|
||||
return {"current_service": current_service}
|
||||
|
||||
@application.context_processor
|
||||
def _attach_current_organization():
|
||||
return {'current_org': current_organization}
|
||||
return {"current_org": current_organization}
|
||||
|
||||
@application.context_processor
|
||||
def _attach_current_user():
|
||||
return {'current_user': current_user}
|
||||
return {"current_user": current_user}
|
||||
|
||||
@application.context_processor
|
||||
def _nav_selected():
|
||||
@@ -313,13 +289,28 @@ def init_app(application):
|
||||
|
||||
@application.context_processor
|
||||
def _attach_current_global_daily_messages():
|
||||
global_limit = 0
|
||||
remaining_global_messages = 0
|
||||
|
||||
if current_app:
|
||||
global_limit = current_app.config['GLOBAL_SERVICE_MESSAGE_LIMIT']
|
||||
global_messages_count = service_api_client.get_global_notification_count()
|
||||
remaining_global_messages = global_limit - global_messages_count
|
||||
return {'daily_global_messages_remaining': remaining_global_messages}
|
||||
if request.view_args:
|
||||
service_id = request.view_args.get(
|
||||
"service_id", session.get("service_id")
|
||||
)
|
||||
else:
|
||||
service_id = session.get("service_id")
|
||||
|
||||
if service_id:
|
||||
global_limit = current_app.config["GLOBAL_SERVICE_MESSAGE_LIMIT"]
|
||||
global_messages_count = (
|
||||
service_api_client.get_global_notification_count(service_id)
|
||||
)
|
||||
remaining_global_messages = global_limit - global_messages_count.get(
|
||||
"count"
|
||||
)
|
||||
return {
|
||||
"global_message_limit": global_limit,
|
||||
"daily_global_messages_remaining": remaining_global_messages,
|
||||
}
|
||||
|
||||
@application.before_request
|
||||
def record_start_time():
|
||||
@@ -329,16 +320,16 @@ def init_app(application):
|
||||
@application.context_processor
|
||||
def inject_global_template_variables():
|
||||
return {
|
||||
'asset_path': application.config['ASSET_PATH'],
|
||||
'header_colour': application.config['HEADER_COLOUR'],
|
||||
'asset_url': asset_fingerprinter.get_url,
|
||||
'font_paths': font_paths,
|
||||
"asset_path": application.config["ASSET_PATH"],
|
||||
"header_colour": application.config["HEADER_COLOUR"],
|
||||
"asset_url": asset_fingerprinter.get_url,
|
||||
"font_paths": font_paths,
|
||||
}
|
||||
|
||||
application.url_map.converters['uuid'].to_python = lambda self, value: value
|
||||
application.url_map.converters['template_type'] = TemplateTypeConverter
|
||||
application.url_map.converters['ticket_type'] = TicketTypeConverter
|
||||
application.url_map.converters['simple_date'] = SimpleDateTypeConverter
|
||||
application.url_map.converters["uuid"].to_python = lambda self, value: value
|
||||
application.url_map.converters["template_type"] = TemplateTypeConverter
|
||||
application.url_map.converters["ticket_type"] = TicketTypeConverter
|
||||
application.url_map.converters["simple_date"] = SimpleDateTypeConverter
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
@@ -350,7 +341,7 @@ def make_session_permanent():
|
||||
"""
|
||||
Make sessions permanent. By permanent, we mean "admin app sets when it expires". Normally the cookie would expire
|
||||
whenever you close the browser. With this, the session expiry is set in `config['PERMANENT_SESSION_LIFETIME']`
|
||||
(20 hours) and is refreshed after every request. IE: you will be logged out after twenty hours of inactivity.
|
||||
(30 min) and is refreshed after every request. IE: you will be logged out after thirty minutes of inactivity.
|
||||
|
||||
We don't _need_ to set this every request (it's saved within the cookie itself under the `_permanent` flag), only
|
||||
when you first log in/sign up/get invited/etc, but we do it just to be safe. For more reading, check here:
|
||||
@@ -359,22 +350,39 @@ def make_session_permanent():
|
||||
session.permanent = True
|
||||
|
||||
|
||||
def create_beta_url(url):
|
||||
url_created = urlparse(url)
|
||||
url_list = list(url_created)
|
||||
url_list[1] = "beta.notify.gov"
|
||||
url_for_redirect = urlunparse(url_list)
|
||||
return url_for_redirect
|
||||
|
||||
|
||||
def redirect_notify_to_beta():
|
||||
if (
|
||||
current_app.config["NOTIFY_ENVIRONMENT"] == "production"
|
||||
and "beta.notify.gov" not in request.url
|
||||
):
|
||||
url_to_beta = create_beta_url(request.url)
|
||||
return redirect(url_to_beta, 302)
|
||||
|
||||
|
||||
def load_service_before_request():
|
||||
if '/static/' in request.url:
|
||||
if "/static/" in request.url:
|
||||
request_ctx.service = None
|
||||
return
|
||||
if request_ctx is not None:
|
||||
request_ctx.service = None
|
||||
|
||||
if request.view_args:
|
||||
service_id = request.view_args.get('service_id', session.get('service_id'))
|
||||
service_id = request.view_args.get("service_id", session.get("service_id"))
|
||||
else:
|
||||
service_id = session.get('service_id')
|
||||
service_id = session.get("service_id")
|
||||
|
||||
if service_id:
|
||||
try:
|
||||
request_ctx.service = Service(
|
||||
service_api_client.get_service(service_id)['data']
|
||||
service_api_client.get_service(service_id)["data"]
|
||||
)
|
||||
except HTTPError as exc:
|
||||
# if service id isn't real, then 404 rather than 500ing later because we expect service to be set
|
||||
@@ -385,14 +393,14 @@ def load_service_before_request():
|
||||
|
||||
|
||||
def load_organization_before_request():
|
||||
if '/static/' in request.url:
|
||||
if "/static/" in request.url:
|
||||
request_ctx.organization = None
|
||||
return
|
||||
if request_ctx is not None:
|
||||
request_ctx.organization = None
|
||||
|
||||
if request.view_args:
|
||||
org_id = request.view_args.get('org_id')
|
||||
org_id = request.view_args.get("org_id")
|
||||
|
||||
if org_id:
|
||||
try:
|
||||
@@ -407,15 +415,19 @@ def load_organization_before_request():
|
||||
|
||||
def save_service_or_org_after_request(response):
|
||||
# Only save the current session if the request is 200
|
||||
service_id = request.view_args.get('service_id', None) if request.view_args else None
|
||||
organization_id = request.view_args.get('org_id', None) if request.view_args else None
|
||||
service_id = (
|
||||
request.view_args.get("service_id", None) if request.view_args else None
|
||||
)
|
||||
organization_id = (
|
||||
request.view_args.get("org_id", None) if request.view_args else None
|
||||
)
|
||||
if response.status_code == 200:
|
||||
if service_id:
|
||||
session['service_id'] = service_id
|
||||
session['organization_id'] = None
|
||||
session["service_id"] = service_id
|
||||
session["organization_id"] = None
|
||||
elif organization_id:
|
||||
session['service_id'] = None
|
||||
session['organization_id'] = organization_id
|
||||
session["service_id"] = None
|
||||
session["organization_id"] = organization_id
|
||||
return response
|
||||
|
||||
|
||||
@@ -423,32 +435,38 @@ def register_errorhandlers(application): # noqa (C901 too complex)
|
||||
def _error_response(error_code, error_page_template=None):
|
||||
if error_page_template is None:
|
||||
error_page_template = error_code
|
||||
return make_response(render_template("error/{0}.html".format(error_page_template)), error_code)
|
||||
return make_response(
|
||||
render_template("error/{0}.html".format(error_page_template)), error_code
|
||||
)
|
||||
|
||||
@application.errorhandler(HTTPError)
|
||||
def render_http_error(error):
|
||||
application.logger.warning("API {} failed with status {} message {}".format(
|
||||
error.response.url if error.response else 'unknown',
|
||||
error.status_code,
|
||||
error.message
|
||||
))
|
||||
application.logger.warning(
|
||||
"API {} failed with status {} message {}".format(
|
||||
error.response.url if error.response else "unknown",
|
||||
error.status_code,
|
||||
error.message,
|
||||
)
|
||||
)
|
||||
error_code = error.status_code
|
||||
if error_code not in [401, 404, 403, 410]:
|
||||
# probably a 500 or 503.
|
||||
# it might be a 400, which we should handle as if it's an internal server error. If the API might
|
||||
# legitimately return a 400, we should handle that within the view or the client that calls it.
|
||||
application.logger.exception("API {} failed with status {} message {}".format(
|
||||
error.response.url if error.response else 'unknown',
|
||||
error.status_code,
|
||||
error.message
|
||||
))
|
||||
application.logger.exception(
|
||||
"API {} failed with status {} message {}".format(
|
||||
error.response.url if error.response else "unknown",
|
||||
error.status_code,
|
||||
error.message,
|
||||
)
|
||||
)
|
||||
error_code = 500
|
||||
return _error_response(error_code)
|
||||
|
||||
@application.errorhandler(400)
|
||||
def handle_client_error(error):
|
||||
# This is tripped if we call `abort(400)`.
|
||||
application.logger.exception('Unhandled 400 client error')
|
||||
application.logger.exception("Unhandled 400 client error")
|
||||
return _error_response(400, error_page_template=500)
|
||||
|
||||
@application.errorhandler(410)
|
||||
@@ -470,23 +488,24 @@ def register_errorhandlers(application): # noqa (C901 too complex)
|
||||
@application.errorhandler(BadSignature)
|
||||
def handle_bad_token(error):
|
||||
# if someone has a malformed token
|
||||
flash('There’s something wrong with the link you’ve used.')
|
||||
flash("There’s something wrong with the link you’ve used.")
|
||||
return _error_response(404)
|
||||
|
||||
@application.errorhandler(CSRFError)
|
||||
def handle_csrf(reason):
|
||||
application.logger.warning('csrf.error_message: {}'.format(reason))
|
||||
application.logger.warning("csrf.error_message: {}".format(reason))
|
||||
|
||||
if 'user_id' not in session:
|
||||
if "user_id" not in session:
|
||||
application.logger.warning(
|
||||
u'csrf.session_expired: Redirecting user to log in page'
|
||||
"csrf.session_expired: Redirecting user to log in page"
|
||||
)
|
||||
|
||||
return application.login_manager.unauthorized()
|
||||
|
||||
application.logger.warning(
|
||||
u'csrf.invalid_token: Aborting request, user_id: {user_id}',
|
||||
extra={'user_id': session['user_id']})
|
||||
"csrf.invalid_token: Aborting request, user_id: {user_id}",
|
||||
extra={"user_id": session["user_id"]},
|
||||
)
|
||||
|
||||
return _error_response(400, error_page_template=500)
|
||||
|
||||
@@ -505,14 +524,14 @@ def register_errorhandlers(application): # noqa (C901 too complex)
|
||||
@application.errorhandler(InviteTokenError)
|
||||
def handle_bad_invite_token(error):
|
||||
flash(str(error))
|
||||
return redirect(url_for('main.sign_in'))
|
||||
return redirect(url_for("main.sign_in"))
|
||||
|
||||
@application.errorhandler(500)
|
||||
@application.errorhandler(Exception)
|
||||
def handle_bad_request(error):
|
||||
current_app.logger.exception(error)
|
||||
# We want the Flask in browser stacktrace
|
||||
if current_app.config.get('DEBUG', None):
|
||||
if current_app.config.get("DEBUG", None):
|
||||
raise error
|
||||
return _error_response(500)
|
||||
|
||||
@@ -575,13 +594,9 @@ def add_template_filters(application):
|
||||
|
||||
|
||||
def init_jinja(application):
|
||||
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
template_folders = [
|
||||
os.path.join(repo_root, 'app/templates'),
|
||||
os.path.join(repo_root, "app/templates"),
|
||||
]
|
||||
jinja_loader = jinja2.FileSystemLoader(template_folders)
|
||||
application.jinja_loader = jinja_loader
|
||||
|
||||
|
||||
def setup_basic_auth(application):
|
||||
application.basic_auth = CustomBasicAuth(application)
|
||||
|
||||
@@ -3,23 +3,23 @@ import hashlib
|
||||
|
||||
class AssetFingerprinter(object):
|
||||
"""
|
||||
Get a unique hash for an asset file, so that it doesn't stay cached
|
||||
when it changes
|
||||
Get a unique hash for an asset file, so that it doesn't stay cached
|
||||
when it changes
|
||||
|
||||
Usage:
|
||||
Usage:
|
||||
|
||||
in the application
|
||||
template_data.asset_fingerprinter = AssetFingerprinter()
|
||||
in the application
|
||||
template_data.asset_fingerprinter = AssetFingerprinter()
|
||||
|
||||
where template data is how you pass variables to every template.
|
||||
where template data is how you pass variables to every template.
|
||||
|
||||
in template.html:
|
||||
{{ asset_fingerprinter.get_url('stylesheets/application.css') }}
|
||||
in template.html:
|
||||
{{ asset_fingerprinter.get_url('stylesheets/application.css') }}
|
||||
|
||||
* 'app/static' is assumed to be the root for all asset files
|
||||
* 'app/static' is assumed to be the root for all asset files
|
||||
"""
|
||||
|
||||
def __init__(self, asset_root='/static/', filesystem_path='app/static/'):
|
||||
def __init__(self, asset_root="/static/", filesystem_path="app/static/"):
|
||||
self._cache = {}
|
||||
self._asset_root = asset_root
|
||||
self._filesystem_path = filesystem_path
|
||||
@@ -29,10 +29,10 @@ class AssetFingerprinter(object):
|
||||
return self._asset_root + asset_path
|
||||
if asset_path not in self._cache:
|
||||
self._cache[asset_path] = (
|
||||
self._asset_root +
|
||||
asset_path +
|
||||
'?' +
|
||||
self.get_asset_fingerprint(self._filesystem_path + asset_path)
|
||||
self._asset_root
|
||||
+ asset_path
|
||||
+ "?"
|
||||
+ self.get_asset_fingerprint(self._filesystem_path + asset_path)
|
||||
)
|
||||
return self._cache[asset_path]
|
||||
|
||||
@@ -42,7 +42,7 @@ class AssetFingerprinter(object):
|
||||
).hexdigest()
|
||||
|
||||
def get_asset_file_contents(self, asset_file_path):
|
||||
with open(asset_file_path, 'rb') as asset_file:
|
||||
with open(asset_file_path, "rb") as asset_file:
|
||||
contents = asset_file.read()
|
||||
return contents
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="govuk-template ">
|
||||
<html lang="en" class="usa-template">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>
|
||||
Sorry, there is a problem with the service – U.S. Notify
|
||||
Sorry, there is a problem with the service – Notify.gov
|
||||
</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="theme-color" content="#0b0c0c" />
|
||||
@@ -12,50 +12,43 @@
|
||||
|
||||
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="/static/images/favicon.ico?de7abc5226925203ac10b0a4a94af949"
|
||||
type="image/x-icon" />
|
||||
<link rel="mask-icon" href="/static/images/govuk-mask-icon.svg?2afd125b1db96109388d4a0727312f3e" color="#0b0c0c">
|
||||
<link rel="mask-icon" href="/static/images/usa-mask-icon.svg?2afd125b1db96109388d4a0727312f3e" color="#F0F0F0">
|
||||
<link rel="apple-touch-icon" sizes="180x180"
|
||||
href="/static/images/govuk-apple-touch-icon-180x180.png?a0f7e1b728a42016b247dc54ee40d055">
|
||||
<link rel="apple-touch-icon" sizes="167x167"
|
||||
href="/static/images/govuk-apple-touch-icon-167x167.png?f636cb7d471fc6239d2241d75cbdabcb">
|
||||
<link rel="apple-touch-icon" sizes="152x152"
|
||||
href="/static/images/govuk-apple-touch-icon-152x152.png?40846d46aa37232e2b35065769ce567c">
|
||||
<link rel="apple-touch-icon" href="/static/images/govuk-apple-touch-icon.png?3c45daa1f1e716458e4da954b973002e">
|
||||
href="/static/images/apple-touch-icon.png?a0f7e1b728a42016b247dc54ee40d055">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/images/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/images/favicon-16x16.png">
|
||||
<link rel="manifest" href="/static/images/site.webmanifest">
|
||||
|
||||
<link rel="stylesheet" media="screen" href="/static/stylesheets/main.css?d077f86473501ab244de0b30600536ee" />
|
||||
<link rel="stylesheet" media="print" href="/static/stylesheets/print.css?28010888ca5719dc83d7c2c80f6ed2b2" />
|
||||
<style>
|
||||
.govuk-header__container {
|
||||
border-color: #005EA5
|
||||
}
|
||||
</style>
|
||||
|
||||
<meta property="og:image" content="/static/images/govuk-opengraph-image.png">
|
||||
<meta property="og:image" content="/static/images/usa-opengraph-image.png">
|
||||
</head>
|
||||
|
||||
<body class="govuk-template__body ">
|
||||
<a href="#main-content" class="govuk-skip-link">Skip to main content</a>
|
||||
<header class="govuk-header " role="banner" data-module="header">
|
||||
<div class="govuk-header__container govuk-width-container">
|
||||
<body class="usa-template__body">
|
||||
<a href="#main-content" class="usa-skip-link">Skip to main content</a>
|
||||
<header class="usa-header " role="banner" data-module="header">
|
||||
<div class="usa-header__container usa-width-container">
|
||||
|
||||
<div class="govuk-header__logo">
|
||||
<a href="/accounts-or-dashboard" class="govuk-header__link govuk-header__link--homepage">
|
||||
<span class="govuk-header__logotype">
|
||||
<div class="usa-header__logo">
|
||||
<a href="/accounts-or-dashboard" class="usa-header__link usa-header__link--homepage">
|
||||
<span class="usa-header__logotype">
|
||||
|
||||
<svg role="presentation" focusable="false" class="govuk-header__logotype"
|
||||
<svg role="presentation" focusable="false" class="usa-header__logotype"
|
||||
xmlns="http://www.w3.org/2000/svg" viewbox="0 0 132 97" height="32" width="36">
|
||||
<path fill="currentColor" fill-rule="evenodd"
|
||||
d="M25 30.2c3.5 1.5 7.7-.2 9.1-3.7 1.5-3.6-.2-7.8-3.9-9.2-3.6-1.4-7.6.3-9.1 3.9-1.4 3.5.3 7.5 3.9 9zM9 39.5c3.6 1.5 7.8-.2 9.2-3.7 1.5-3.6-.2-7.8-3.9-9.1-3.6-1.5-7.6.2-9.1 3.8-1.4 3.5.3 7.5 3.8 9zM4.4 57.2c3.5 1.5 7.7-.2 9.1-3.8 1.5-3.6-.2-7.7-3.9-9.1-3.5-1.5-7.6.3-9.1 3.8-1.4 3.5.3 7.6 3.9 9.1zm38.3-21.4c3.5 1.5 7.7-.2 9.1-3.8 1.5-3.6-.2-7.7-3.9-9.1-3.6-1.5-7.6.3-9.1 3.8-1.3 3.6.4 7.7 3.9 9.1zm64.4-5.6c-3.6 1.5-7.8-.2-9.1-3.7-1.5-3.6.2-7.8 3.8-9.2 3.6-1.4 7.7.3 9.2 3.9 1.3 3.5-.4 7.5-3.9 9zm15.9 9.3c-3.6 1.5-7.7-.2-9.1-3.7-1.5-3.6.2-7.8 3.7-9.1 3.6-1.5 7.7.2 9.2 3.8 1.5 3.5-.3 7.5-3.8 9zm4.7 17.7c-3.6 1.5-7.8-.2-9.2-3.8-1.5-3.6.2-7.7 3.9-9.1 3.6-1.5 7.7.3 9.2 3.8 1.3 3.5-.4 7.6-3.9 9.1zM89.3 35.8c-3.6 1.5-7.8-.2-9.2-3.8-1.4-3.6.2-7.7 3.9-9.1 3.6-1.5 7.7.3 9.2 3.8 1.4 3.6-.3 7.7-3.9 9.1zM69.7 17.7l8.9 4.7V9.3l-8.9 2.8c-.2-.3-.5-.6-.9-.9L72.4 0H59.6l3.5 11.2c-.3.3-.6.5-.9.9l-8.8-2.8v13.1l8.8-4.7c.3.3.6.7.9.9l-5 15.4v.1c-.2.8-.4 1.6-.4 2.4 0 4.1 3.1 7.5 7 8.1h.2c.3 0 .7.1 1 .1.4 0 .7 0 1-.1h.2c4-.6 7.1-4.1 7.1-8.1 0-.8-.1-1.7-.4-2.4V34l-5.1-15.4c.4-.2.7-.6 1-.9zM66 92.8c16.9 0 32.8 1.1 47.1 3.2 4-16.9 8.9-26.7 14-33.5l-9.6-3.4c1 4.9 1.1 7.2 0 10.2-1.5-1.4-3-4.3-4.2-8.7L108.6 76c2.8-2 5-3.2 7.5-3.3-4.4 9.4-10 11.9-13.6 11.2-4.3-.8-6.3-4.6-5.6-7.9 1-4.7 5.7-5.9 8-.5 4.3-8.7-3-11.4-7.6-8.8 7.1-7.2 7.9-13.5 2.1-21.1-8 6.1-8.1 12.3-4.5 20.8-4.7-5.4-12.1-2.5-9.5 6.2 3.4-5.2 7.9-2 7.2 3.1-.6 4.3-6.4 7.8-13.5 7.2-10.3-.9-10.9-8-11.2-13.8 2.5-.5 7.1 1.8 11 7.3L80.2 60c-4.1 4.4-8 5.3-12.3 5.4 1.4-4.4 8-11.6 8-11.6H55.5s6.4 7.2 7.9 11.6c-4.2-.1-8-1-12.3-5.4l1.4 16.4c3.9-5.5 8.5-7.7 10.9-7.3-.3 5.8-.9 12.8-11.1 13.8-7.2.6-12.9-2.9-13.5-7.2-.7-5 3.8-8.3 7.1-3.1 2.7-8.7-4.6-11.6-9.4-6.2 3.7-8.5 3.6-14.7-4.6-20.8-5.8 7.6-5 13.9 2.2 21.1-4.7-2.6-11.9.1-7.7 8.8 2.3-5.5 7.1-4.2 8.1.5.7 3.3-1.3 7.1-5.7 7.9-3.5.7-9-1.8-13.5-11.2 2.5.1 4.7 1.3 7.5 3.3l-4.7-15.4c-1.2 4.4-2.7 7.2-4.3 8.7-1.1-3-.9-5.3 0-10.2l-9.5 3.4c5 6.9 9.9 16.7 14 33.5 14.8-2.1 30.8-3.2 47.7-3.2z">
|
||||
</path>
|
||||
|
||||
<image src="https://images.emojiterra.com/twitter/v14.0/512px/1f1fa-1f1f8.png" xlink:href=""
|
||||
class="govuk-header__logotype-fallback-image" width="36" height="32"></image>
|
||||
class="usa-header__logotype-fallback-image" width="36" height="32"></image>
|
||||
</svg>
|
||||
<span class="govuk-header__logotype-text">
|
||||
<span class="usa-header__logotype-text">
|
||||
US
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="govuk-header__product-name">
|
||||
<span class="usa-header__product-name">
|
||||
Notify
|
||||
</span>
|
||||
|
||||
@@ -65,18 +58,18 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="govuk-width-container">
|
||||
<main class="govuk-main-wrapper govuk-!-padding-top-0 govuk-!-padding-bottom-12" id="main-content" role="main">
|
||||
<div class="govuk-grid-row">
|
||||
<div class="govuk-grid-column-full">
|
||||
<h1 class="govuk-heading-l govuk-!-padding-top-7">
|
||||
<div class="usa-width-container">
|
||||
<main class="usa-main-wrapper" id="main-content" role="main">
|
||||
<div class="grid-row">
|
||||
<div class="grid-col-12">
|
||||
<h1 class="usa-heading-l">
|
||||
Sorry, there is a problem with the service
|
||||
</h1>
|
||||
<p class="govuk-body">
|
||||
<p class="usa-body">
|
||||
Try again later.
|
||||
</p>
|
||||
<p class="govuk-body">
|
||||
You can check our <a class="govuk-link govuk-link--no-visited-state"
|
||||
<p class="usa-body">
|
||||
You can check our <a class="usa-link"
|
||||
href="https://status.notifications.service.gov.uk/">system status</a> page to see if there are any known
|
||||
issues.<br />To report a problem, email <a
|
||||
href="mailto:gov-uk-notify-support@digital.cabinet-office.gov.uk">gov-uk-notify-support@digital.cabinet-office.gov.uk</a>
|
||||
@@ -85,25 +78,25 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer class="govuk-footer " role="contentinfo">
|
||||
<div class="govuk-width-container ">
|
||||
<div class="govuk-footer__meta">
|
||||
<div class="govuk-footer__meta-item govuk-footer__meta-item--grow">
|
||||
<footer class="usa-footer " role="contentinfo">
|
||||
<div class="usa-width-container ">
|
||||
<div class="usa-footer__meta">
|
||||
<div class="usa-footer__meta-item usa-footer__meta-item--grow">
|
||||
|
||||
<h2 class="usa-sr-only">Support links</h2>
|
||||
<div class="govuk-footer__meta-custom">
|
||||
<div class="usa-footer__meta-custom">
|
||||
Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions"
|
||||
class="govuk-footer__link">Technology Transformation Services</a>
|
||||
class="usa-footer__link">Technology Transformation Services</a>
|
||||
</div>
|
||||
|
||||
<svg role="presentation" focusable="false" class="govuk-footer__licence-logo"
|
||||
<svg role="presentation" focusable="false" class="usa-footer__licence-logo"
|
||||
xmlns="http://www.w3.org/2000/svg" viewbox="0 0 483.2 195.7" height="17" width="41">
|
||||
<path fill="currentColor"
|
||||
d="M421.5 142.8V.1l-50.7 32.3v161.1h112.4v-50.7zm-122.3-9.6A47.12 47.12 0 0 1 221 97.8c0-26 21.1-47.1 47.1-47.1 16.7 0 31.4 8.7 39.7 21.8l42.7-27.2A97.63 97.63 0 0 0 268.1 0c-36.5 0-68.3 20.1-85.1 49.7A98 98 0 0 0 97.8 0C43.9 0 0 43.9 0 97.8s43.9 97.8 97.8 97.8c36.5 0 68.3-20.1 85.1-49.7a97.76 97.76 0 0 0 149.6 25.4l19.4 22.2h3v-87.8h-80l24.3 27.5zM97.8 145c-26 0-47.1-21.1-47.1-47.1s21.1-47.1 47.1-47.1 47.2 21 47.2 47S123.8 145 97.8 145" />
|
||||
</svg>
|
||||
<span class="govuk-footer__licence-description">
|
||||
<span class="usa-footer__licence-description">
|
||||
All content is available under the
|
||||
<a class="govuk-footer__link"
|
||||
<a class="usa-footer__link"
|
||||
href="https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" rel="license">Open
|
||||
Government Licence v3.0</a>, except where otherwise stated
|
||||
</span>
|
||||
|
||||
BIN
app/assets/images/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/assets/images/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
app/assets/images/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/assets/images/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 283 B |
BIN
app/assets/images/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 384 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 133 KiB |
1
app/assets/images/site.webmanifest
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
4
app/assets/images/usa-mask-icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="680" height="680" viewBox="0 0 680 680" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M672 211H332V161L672 161V211ZM672 294H332V244H672V294ZM672 377H332V327H672V377ZM672 410V460H8V410H672ZM672 493V543H8V493H672Z" fill="#D83933"/>
|
||||
<path d="M8 161H299V377H8V161Z" fill="#3C3B6E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 349 B |
BIN
app/assets/images/usa-opengraph-image.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -1,74 +0,0 @@
|
||||
(function (window) {
|
||||
"use strict";
|
||||
|
||||
window.GOVUK.Modules.AuthenticateSecurityKey = function () {
|
||||
this.start = function (component) {
|
||||
$(component)
|
||||
.on('click', function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
// hide any existing error prompt
|
||||
window.GOVUK.ErrorBanner.hideBanner();
|
||||
|
||||
fetch('/webauthn/authenticate')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw Error(response.statusText);
|
||||
}
|
||||
|
||||
return response.arrayBuffer();
|
||||
})
|
||||
.then(data => {
|
||||
var options = window.CBOR.decode(data);
|
||||
// triggers browser dialogue to login with authenticator
|
||||
return window.navigator.credentials.get(options);
|
||||
})
|
||||
.then(credential => {
|
||||
const currentURL = new URL(window.location.href);
|
||||
|
||||
// create authenticateURL from admin hostname plus /webauthn/authenticate path
|
||||
const authenticateURL = new URL('/webauthn/authenticate', window.location.href);
|
||||
|
||||
const nextUrl = currentURL.searchParams.get('next');
|
||||
if (nextUrl) {
|
||||
// takes nextUrl from the query string on the current browser URL
|
||||
// (which should be /two-factor-webauthn) and pass it through to
|
||||
// the POST. put it in a query string so it's consistent with how
|
||||
// the other login flows manage it
|
||||
authenticateURL.searchParams.set('next', nextUrl);
|
||||
}
|
||||
|
||||
return fetch(authenticateURL, {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': component.data('csrfToken') },
|
||||
body: window.CBOR.encode({
|
||||
credentialId: new Uint8Array(credential.rawId),
|
||||
authenticatorData: new Uint8Array(credential.response.authenticatorData),
|
||||
signature: new Uint8Array(credential.response.signature),
|
||||
clientDataJSON: new Uint8Array(credential.response.clientDataJSON),
|
||||
})
|
||||
});
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw Error(response.statusText);
|
||||
}
|
||||
|
||||
return response.arrayBuffer();
|
||||
})
|
||||
.then(cbor => {
|
||||
return Promise.resolve(window.CBOR.decode(cbor));
|
||||
})
|
||||
.then(data => {
|
||||
window.location.assign(data.redirect_url);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
// some browsers will show an error dialogue for some
|
||||
// errors; to be safe we always display an error message on the page.
|
||||
window.GOVUK.ErrorBanner.showBanner();
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
}) (window);
|
||||
@@ -20,7 +20,7 @@
|
||||
}[field] || `No ${field}s`)
|
||||
};
|
||||
Summary.prototype.addContent = function() {
|
||||
const $hint = this.module.$formGroup.find('.govuk-hint');
|
||||
const $hint = this.module.$formGroup.find('.usa-hint');
|
||||
this.$text = $(`<p class="selection-summary__text" />`);
|
||||
|
||||
if (this.fieldLabel === 'folder') { this.$text.addClass('selection-summary__text--folders'); }
|
||||
@@ -90,7 +90,7 @@
|
||||
};
|
||||
CollapsibleCheckboxes.prototype.start = function(component) {
|
||||
this.$component = $(component);
|
||||
this.$formGroup = this.$component.find('.govuk-form-group').first();
|
||||
this.$formGroup = this.$component.find('.usa-form-group').first();
|
||||
this.$fieldset = this.$formGroup.find('fieldset').first();
|
||||
this.$checkboxes = this.$fieldset.find('input[type=checkbox]');
|
||||
this.fieldLabel = this.$component.data('fieldLabel');
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
this.$input = $(component);
|
||||
|
||||
this.$input.closest('.govuk-form-group').append(
|
||||
this.$input.closest('.usa-form-group').append(
|
||||
this.$preview = $('<span class="textbox-colour-preview"></span>')
|
||||
);
|
||||
|
||||
|
||||
71
app/assets/javascripts/dataVisualization.js
Normal file
@@ -0,0 +1,71 @@
|
||||
(function (window) {
|
||||
|
||||
// Dummy data (replace with API data)
|
||||
const data = {
|
||||
messageStats: {
|
||||
totalMessages: 1000,
|
||||
delivered: 800,
|
||||
pending: 100,
|
||||
failed: 100
|
||||
},
|
||||
dailyUsage: {
|
||||
dailyUsage: 20,
|
||||
dailyUsageLimit: 100
|
||||
},
|
||||
yearlyUsage: {
|
||||
yearlyUsage: 2000,
|
||||
yearlyUsageLimit: 250000
|
||||
}
|
||||
};
|
||||
|
||||
// Update total messages progress bar percentages
|
||||
const messageStats = data.messageStats;
|
||||
|
||||
const messageBars = ["delivered", "pending", "failed"];
|
||||
for (const bar of messageBars) {
|
||||
const percentage = messageStats[bar] / messageStats.totalMessages * 100;
|
||||
const elementId = `${bar}-bar`;
|
||||
const element = document.getElementById(elementId);
|
||||
element.style.width = `${parseFloat(percentage)}%`;
|
||||
}
|
||||
|
||||
// Update total messages progress bar percentages
|
||||
const dailyUsage = data.dailyUsage;
|
||||
const dailyUsageLimit = dailyUsage.dailyUsageLimit;
|
||||
const yearlyUsage = data.yearlyUsage;
|
||||
const yearlyUsageLimit = yearlyUsage.yearlyUsageLimit;
|
||||
|
||||
function updateUsageBar(elementId, dailyUsage, dailyUsageLimit, yearlyUsage, yearlyUsageLimit) {
|
||||
// Ensure element exists
|
||||
if (!elementId || !document.getElementById(elementId)) {
|
||||
console.error(`Element with ID "${elementId}" not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate percentage
|
||||
const percentage = dailyUsage / dailyUsageLimit * 100;
|
||||
|
||||
// Update bar width
|
||||
const element = document.getElementById(elementId);
|
||||
element.style.width = `${parseFloat(percentage)}%`;
|
||||
}
|
||||
|
||||
// Update usage bars
|
||||
updateUsageBar("dailyUsage-bar", dailyUsage.dailyUsage, dailyUsageLimit);
|
||||
updateUsageBar("dailyUsageRemaining-bar", dailyUsageLimit - dailyUsage.dailyUsage, dailyUsageLimit);
|
||||
updateUsageBar("yearlyUsage-bar", yearlyUsage.yearlyUsage, yearlyUsageLimit);
|
||||
updateUsageBar("yearlyUsageRemaining-bar", yearlyUsageLimit - yearlyUsage.yearlyUsage, yearlyUsageLimit);
|
||||
|
||||
// Update total messages legend values
|
||||
document.getElementById("total-value").innerText = `Total: ${messageStats.totalMessages} messages`;
|
||||
document.getElementById("delivered-value").innerText = `Delivered: ${messageStats.delivered}`;
|
||||
document.getElementById("pending-value").innerText = `Pending: ${messageStats.pending}`;
|
||||
document.getElementById("failed-value").innerText = `Failed: ${messageStats.failed}`;
|
||||
|
||||
// Update usage legend values
|
||||
document.getElementById("daily-usage-value").innerText = dailyUsage.dailyUsage;
|
||||
document.getElementById("daily-remaining-value").innerText = dailyUsage.dailyUsageLimit - dailyUsage.dailyUsage;
|
||||
document.getElementById("yearly-usage-value").innerText = yearlyUsage.yearlyUsage;
|
||||
document.getElementById("yearly-remaining-value").innerText = yearlyUsage.yearlyUsageLimit - yearlyUsage.yearlyUsage;
|
||||
|
||||
})(window);
|
||||
10
app/assets/javascripts/date.js
Normal file
@@ -0,0 +1,10 @@
|
||||
(function (window) {
|
||||
"use strict";
|
||||
|
||||
// Show the current year
|
||||
const currentYearElement = document.getElementById("current-year");
|
||||
|
||||
if (currentYearElement) {
|
||||
currentYearElement.textContent = new Date().getFullYear();
|
||||
}
|
||||
})(window);
|
||||
@@ -9,9 +9,8 @@
|
||||
This may behave in unexpected ways if you have more than one element with the `banner-dangerous` class on your page.
|
||||
*/
|
||||
window.GOVUK.ErrorBanner = {
|
||||
hideBanner: () => $('.banner-dangerous').addClass('govuk-!-display-none'),
|
||||
hideBanner: () => $('.banner-dangerous').addClass('display-none'),
|
||||
showBanner: () => $('.banner-dangerous')
|
||||
.removeClass('govuk-!-display-none')
|
||||
.trigger('focus'),
|
||||
.removeClass('display-none')
|
||||
};
|
||||
})(window);
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
|
||||
// The label gets styled like a button and is used to hide the native file upload control. This is so that
|
||||
// users see a button that looks like the others on the site.
|
||||
//
|
||||
this.$form.find('label.file-upload-button').addClass('usa-button');
|
||||
|
||||
this.$form.find('label.file-upload-button').addClass('usa-button margin-bottom-1').attr( {role: 'button', tabindex: '0'} );
|
||||
|
||||
// Clear the form if the user navigates back to the page
|
||||
$(window).on("pageshow", () => this.$form[0].reset());
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ $(() => GOVUK.modules.start());
|
||||
|
||||
$(() => $('.error-message, .usa-error-message').eq(0).parent('label').next('input').trigger('focus'));
|
||||
|
||||
$(() => $('.banner-dangerous').eq(0).trigger('focus'));
|
||||
|
||||
$(() => $('.govuk-header__container').on('click', function() {
|
||||
$(this).css('border-color', '#005ea5');
|
||||
}));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
$ = global.jQuery;
|
||||
|
||||
let branding_style = $('.govuk-radios__item input[name="branding_style"]:checked');
|
||||
let branding_style = $('.usa-radio input[name="branding_style"]:checked');
|
||||
|
||||
if (!branding_style.length) { return; }
|
||||
|
||||
|
||||
@@ -9,57 +9,57 @@
|
||||
let states = {
|
||||
'initial': Hogan.compile(`
|
||||
{{#showNowAsDefault}}
|
||||
<div class="radio-select__column">
|
||||
<div class="govuk-radios__item">
|
||||
<input class="govuk-radios__input" checked="checked" id="{{name}}-0" name="{{name}}" type="radio" value="">
|
||||
<label class="govuk-label govuk-radios__label" for="{{name}}-0">Now</label>
|
||||
<div class="radio-select__column margin-y-2">
|
||||
<div class="usa-radio">
|
||||
<input class="usa-radio__input" checked="checked" id="{{name}}-0" name="{{name}}" type="radio" value="">
|
||||
<label class="usa-radio__label" for="{{name}}-0">Now</label>
|
||||
</div>
|
||||
</div>
|
||||
{{/showNowAsDefault}}
|
||||
<div class="radio-select__column">
|
||||
<div class="radio-select__column margin-y-2">
|
||||
{{#categories}}
|
||||
<input type='button' class='govuk-button govuk-button--secondary radio-select__button--category' aria-expanded="false" value='{{.}}' />
|
||||
<input type='button' class='usa-button usa-button--outline radio-select__button--category' aria-expanded="false" value='{{.}}' />
|
||||
{{/categories}}
|
||||
</div>
|
||||
`),
|
||||
'choose': Hogan.compile(`
|
||||
{{#showNowAsDefault}}
|
||||
<div class="radio-select__column">
|
||||
<div class="govuk-radios__item">
|
||||
<input class="govuk-radios__input" checked="checked" id="{{name}}-0" name="{{name}}" type="radio" value="">
|
||||
<label class="govuk-label govuk-radios__label" for="{{name}}-0">Now</label>
|
||||
<div class="radio-select__column margin-y-2">
|
||||
<div class="usa-radio">
|
||||
<input class="usa-radio__input" checked="checked" id="{{name}}-0" name="{{name}}" type="radio" value="">
|
||||
<label class="usa-radio__label" for="{{name}}-0">Now</label>
|
||||
</div>
|
||||
</div>
|
||||
{{/showNowAsDefault}}
|
||||
<div class="radio-select__column">
|
||||
<div class="radio-select__column margin-y-2">
|
||||
{{#choices}}
|
||||
<div class="govuk-radios__item js-option">
|
||||
<input class="govuk-radios__input" type="radio" value="{{value}}" id="{{id}}" name="{{name}}" />
|
||||
<label class="govuk-label govuk-radios__label" for="{{id}}">{{label}}</label>
|
||||
<div class="usa-radio js-option">
|
||||
<input class="usa-radio__input" type="radio" value="{{value}}" id="{{id}}" name="{{name}}" />
|
||||
<label class="usa-radio__label" for="{{id}}">{{label}}</label>
|
||||
</div>
|
||||
{{/choices}}
|
||||
<input type='button' class='govuk-button govuk-button--secondary radio-select__button--done' aria-expanded='true' value='Done' />
|
||||
<input type='button' class='usa-button usa-button--outline radio-select__button--done' aria-expanded='true' value='Done' />
|
||||
</div>
|
||||
`),
|
||||
'chosen': Hogan.compile(`
|
||||
{{#showNowAsDefault}}
|
||||
<div class="radio-select__column">
|
||||
<div class="govuk-radios__item">
|
||||
<input class="govuk-radios__input" id="{{name}}-0" name="{{name}}" type="radio" value="">
|
||||
<label class="govuk-label govuk-radios__label" for="{{name}}-0">Now</label>
|
||||
<div class="radio-select__column margin-y-2">
|
||||
<div class="usa-radio">
|
||||
<input class="usa-radio__input" id="{{name}}-0" name="{{name}}" type="radio" value="">
|
||||
<label class="usa-radio__label" for="{{name}}-0">Now</label>
|
||||
</div>
|
||||
</div>
|
||||
{{/showNowAsDefault}}
|
||||
<div class="radio-select__column">
|
||||
<div class="radio-select__column margin-y-2">
|
||||
{{#choices}}
|
||||
<div class="govuk-radios__item">
|
||||
<input class="govuk-radios__input" checked="checked" type="radio" value="{{value}}" id="{{id}}" name="{{name}}" />
|
||||
<label class="govuk-label govuk-radios__label" for="{{id}}">{{label}}</label>
|
||||
<div class="usa-radio">
|
||||
<input class="usa-radio__input" checked="checked" type="radio" value="{{value}}" id="{{id}}" name="{{name}}" />
|
||||
<label class="usa-radio__label" for="{{id}}">{{label}}</label>
|
||||
</div>
|
||||
{{/choices}}
|
||||
</div>
|
||||
<div class="radio-select__column">
|
||||
<input type='button' class='govuk-button govuk-button--secondary radio-select__button--reset' aria-expanded='false' value='Choose a different time' />
|
||||
<div class="radio-select__column margin-y-2">
|
||||
<input type='button' class='usa-button usa-button--outline radio-select__button--reset' aria-expanded='false' value='Choose a different time' />
|
||||
</div>
|
||||
`)
|
||||
};
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
window.GOVUK.Modules.RegisterSecurityKey = function() {
|
||||
this.start = function(component) {
|
||||
$(component)
|
||||
.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
// hide any existing error prompt
|
||||
window.GOVUK.ErrorBanner.hideBanner();
|
||||
|
||||
fetch('/webauthn/register')
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw Error(response.statusText);
|
||||
}
|
||||
|
||||
return response.arrayBuffer();
|
||||
})
|
||||
.then((data) => {
|
||||
var options = window.CBOR.decode(data);
|
||||
// triggers browser dialogue to select authenticator
|
||||
return window.navigator.credentials.create(options);
|
||||
})
|
||||
.then((credential) => {
|
||||
return postWebAuthnCreateResponse(
|
||||
credential.response, component.data('csrfToken')
|
||||
);
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw Error(response.statusText);
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
// some browsers will show an error dialogue for some
|
||||
// errors; to be safe we always display an error message on the page.
|
||||
window.GOVUK.ErrorBanner.showBanner();
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
function postWebAuthnCreateResponse(response, csrf_token) {
|
||||
return fetch('/webauthn/register', {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': csrf_token },
|
||||
body: window.CBOR.encode({
|
||||
attestationObject: new Uint8Array(response.attestationObject),
|
||||
clientDataJSON: new Uint8Array(response.clientDataJSON),
|
||||
})
|
||||
});
|
||||
}
|
||||
})(window);
|
||||
65
app/assets/javascripts/timeoutPopup.js
Normal file
@@ -0,0 +1,65 @@
|
||||
window.GOVUK = window.GOVUK || {};
|
||||
window.GOVUK.Modules = window.GOVUK.Modules || {};
|
||||
window.GOVUK.Modules.TimeoutPopup = window.GOVUK.Modules.TimeoutPopup || {};
|
||||
|
||||
(function(global) {
|
||||
"use strict";
|
||||
|
||||
const sessionTimer = document.getElementById("sessionTimer");
|
||||
let intervalId = null;
|
||||
|
||||
function checkTimer(timeTillSessionEnd) {
|
||||
var now = new Date().getTime();
|
||||
var difference = timeTillSessionEnd - now;
|
||||
var minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60));
|
||||
var seconds = Math.floor((difference % (1000 * 60)) / 1000);
|
||||
document.getElementById("timeLeft").innerHTML = + minutes + "m " + seconds + "s";
|
||||
showTimer();
|
||||
document.getElementById("logOutTimer").addEventListener("click", signoutUser);
|
||||
document.getElementById("extendSessionTimer").addEventListener("click", extendSession);
|
||||
if (difference < 0) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
closeTimer();
|
||||
expireUserSession();
|
||||
}
|
||||
}
|
||||
|
||||
function expireUserSession() {
|
||||
var signOutLink = '/sign-out?next=' + window.location.pathname;
|
||||
window.location.href = signOutLink;
|
||||
|
||||
}
|
||||
|
||||
function signoutUser() {
|
||||
window.location.href = '/sign-out';
|
||||
}
|
||||
|
||||
function extendSession() {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
function showTimer() {
|
||||
sessionTimer.showModal();
|
||||
}
|
||||
|
||||
function closeTimer() {
|
||||
sessionTimer.close();
|
||||
}
|
||||
|
||||
function setSessionTimer() {
|
||||
var timeTillSessionEnd = new Date().getTime() + (5 * 60 * 1000);
|
||||
intervalId = setInterval(checkTimer, 1000, timeTillSessionEnd);
|
||||
}
|
||||
|
||||
if (document.getElementById("timeLeft") !== null) {
|
||||
setTimeout(setSessionTimer, 25 * 60 * 1000);
|
||||
}
|
||||
|
||||
global.GOVUK.Modules.TimeoutPopup.checkTimer = checkTimer;
|
||||
global.GOVUK.Modules.TimeoutPopup.expireUserSession = expireUserSession;
|
||||
global.GOVUK.Modules.TimeoutPopup.signoutUser = signoutUser;
|
||||
global.GOVUK.Modules.TimeoutPopup.extendSession = extendSession;
|
||||
global.GOVUK.Modules.TimeoutPopup.showTimer = showTimer;
|
||||
global.GOVUK.Modules.TimeoutPopup.closeTimer = closeTimer;
|
||||
})(window);
|
||||
67
app/assets/sass/uswds/_data-visualization.scss
Normal file
@@ -0,0 +1,67 @@
|
||||
@use "uswds-core" as *;
|
||||
|
||||
$delivered: color('blue-50v');
|
||||
$pending: color('green-cool-40v');
|
||||
$failed: color('orange-30v');
|
||||
|
||||
.chart-container {
|
||||
display: flex;
|
||||
height: units(10);
|
||||
&.usage {
|
||||
height: units(5);
|
||||
}
|
||||
}
|
||||
|
||||
.bar {
|
||||
&.delivered, &.usage {
|
||||
background-color: $delivered;
|
||||
margin-right: 1px;
|
||||
}
|
||||
&.pending{
|
||||
background-color: $pending;
|
||||
margin-right: 1px;
|
||||
}
|
||||
&.failed, &.remaining {
|
||||
background-color: $failed;
|
||||
}
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
margin: units(1) 0;
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: units(2);
|
||||
}
|
||||
.legend-color {
|
||||
width: units(3);
|
||||
height: units(3);
|
||||
margin-right: units(1);
|
||||
background-color: $delivered;
|
||||
&.pending {
|
||||
background-color: $pending;
|
||||
}
|
||||
&.failed, &.remaining {
|
||||
background-color: $failed;
|
||||
}
|
||||
}
|
||||
.legend-label {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.progress-bar {
|
||||
width: 300px;
|
||||
height: 20px;
|
||||
background-color: #eee;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.progress-bar-inner {
|
||||
height: 100%;
|
||||
background-color: #007bff;
|
||||
border-radius: inherit;
|
||||
}
|
||||
342
app/assets/sass/uswds/_legacy-styles.scss
Normal file
@@ -0,0 +1,342 @@
|
||||
@use "uswds-core" as *;
|
||||
|
||||
// SMS Box Style
|
||||
|
||||
.sms-message-wrapper {
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 464px;
|
||||
box-sizing: border-box;
|
||||
padding: units(1);
|
||||
background: color('gray-cool-10');
|
||||
border: 1px solid color('gray-cool-10');
|
||||
border-radius: 5px;
|
||||
white-space: normal;
|
||||
margin: 0 0 units(1) 0;
|
||||
clear: both;
|
||||
word-wrap: break-word;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
right: -20px;
|
||||
border: 10px solid transparent;
|
||||
border-left-width: 13px;
|
||||
border-right-width: 13px;
|
||||
border-bottom-color: color('gray-cool-10');
|
||||
border-left-color: color('gray-cool-10');
|
||||
transform: rotate(17deg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sms-message-inbound {
|
||||
|
||||
.sms-message-wrapper {
|
||||
|
||||
&:after {
|
||||
border-left-color: transparent;
|
||||
border-bottom-color: color('gray-cool-10');
|
||||
border-right-color: color('gray-cool-10');
|
||||
right: auto;
|
||||
left: -20px;
|
||||
transform: rotate(17deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sms-message-sender {
|
||||
margin: units(1) 0 0;
|
||||
}
|
||||
|
||||
.sms-message-recipient {
|
||||
color: color('gray-cool-90');
|
||||
margin: 0 0 units(1);
|
||||
}
|
||||
|
||||
.sms-message-status {
|
||||
color: color('gray-cool-90');
|
||||
margin: -20px units(1) 20px units(1);
|
||||
}
|
||||
|
||||
.sms-message-status-outbound {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sms-message-row {
|
||||
&:focus {
|
||||
outline: none;
|
||||
padding-top: 120px;
|
||||
margin-top: -120px;
|
||||
}
|
||||
}
|
||||
|
||||
.sms-message-reply-link {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
// File upload area
|
||||
|
||||
.page-footer {
|
||||
position: relative;
|
||||
margin-bottom: 30px;
|
||||
&-link {
|
||||
line-height: 40px;
|
||||
padding: 1px 0 0 15px;
|
||||
font-weight: normal;
|
||||
&:first-of-type {
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&-delete-link-without-button {
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-secondary-link {
|
||||
display: block;
|
||||
margin-top: units(1);
|
||||
}
|
||||
|
||||
&-right-aligned-link {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 9px; // align baseline with buttons
|
||||
&-without-button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.js-cancel {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__button--centred {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.file-upload {
|
||||
&-label,
|
||||
&-button-label {
|
||||
font-weight: bold;
|
||||
font-size: 19px;
|
||||
display: block;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
&-label .error-message,
|
||||
&-button-label.error-message {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&-field {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
// Hide normal upload form if we're adding a custom version with JS
|
||||
.js-enabled &-label,
|
||||
.js-enabled &-field,
|
||||
.js-enabled &-submit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-alternate-link {
|
||||
display: inline-block;
|
||||
line-height: 35px;
|
||||
a {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spreadsheet {
|
||||
|
||||
margin-bottom: units (1);
|
||||
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.usa-table--borderless thead th {
|
||||
border-top: 1px solid color('gray-cool-10');
|
||||
}
|
||||
|
||||
th,
|
||||
.table-field-index {
|
||||
background: color('gray-cool-10');
|
||||
border: 1px solid color('gray-cool-10');
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border: 1px solid color('gray-cool-10');
|
||||
}
|
||||
|
||||
td {
|
||||
border-top: 0;
|
||||
// 194 is the width of the table * 1/3.5, so the overflow cuts off
|
||||
// at 3.5 columns wide.
|
||||
// 11 accounts for the padding of the table cell
|
||||
min-width: 194px - 11px;
|
||||
&:first-child {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.fullscreen-fixed-table {
|
||||
z-index: 1000;
|
||||
.table-field-heading-first {
|
||||
background: color('gray-cool-10');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fullscreen {
|
||||
&-content {
|
||||
background: #FFFFFF;
|
||||
z-index: 10;
|
||||
overflow-y: hidden;
|
||||
box-sizing: border-box;
|
||||
margin: 0 0 units(1) 0;
|
||||
padding: 0 0 0 0;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid color('gray-cool-10');
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
tr:last-child {
|
||||
td {
|
||||
border-bottom: 1px solid #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
th,
|
||||
.table-field-error-label,
|
||||
.table-field-left-aligned {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&-right-shadow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
z-index: 200;
|
||||
&.visible {
|
||||
&.with-transition {
|
||||
transition: box-shadow 0.6s ease-out;
|
||||
}
|
||||
box-shadow: inset -1px 0 0 0 color('gray-cool-10'), inset -3px 0 0 0 rgba(color('gray-cool-10'), 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&-scrollable-table {
|
||||
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
.table-field-heading-first,
|
||||
.table-field-index {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-field-left-aligned {
|
||||
position: relative;
|
||||
z-index: 150;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar:horizontal {
|
||||
height: 11px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 8px;
|
||||
border: 2px solid #FFFFFF;
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-fixed-table {
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
|
||||
.table-field-heading {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.table-field-left-aligned {
|
||||
width: 0;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.table-field-heading-first,
|
||||
.table-field-index {
|
||||
transition: none;
|
||||
position: relative;
|
||||
z-index: 200;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
&-scrolled-table {
|
||||
padding-bottom: 20px;
|
||||
.table-field-heading-first,
|
||||
.table-field-index {
|
||||
transition: box-shadow 0.3s ease-in-out;
|
||||
box-shadow: 1px 0 0 0 color('gray-cool-10'), 3px 0 0 0 rgba(color('gray-cool-10'), 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&-shim {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 9;
|
||||
margin-bottom: 30px;
|
||||
pointer-events: none;
|
||||
& + .table-show-more-link {
|
||||
margin-top: -20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.js-focus-style {
|
||||
outline: 3px solid color("blue-40v");
|
||||
box-shadow: 0 0 0 7px color("blue-40v");
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,12 @@ i.e.
|
||||
|
||||
@use "uswds-core" as *;
|
||||
|
||||
.usa-logo {
|
||||
.usa-header--extended .usa-logo {
|
||||
font-family: family("sans");
|
||||
margin: units(4) 0;
|
||||
@include at-media-max('mobile-lg') {
|
||||
margin: units(4) 0 units(4) units(2);
|
||||
}
|
||||
img {
|
||||
@include at-media($theme-header-min-width) {
|
||||
width: 80px;
|
||||
@@ -32,28 +36,27 @@ i.e.
|
||||
}
|
||||
}
|
||||
|
||||
.usa-nav__primary > .usa-nav__primary-item > a {
|
||||
font-size: size("body", 4);
|
||||
.usa-banner__header-action {
|
||||
font-size: size("body", 2);
|
||||
}
|
||||
|
||||
.usa-nav__primary {
|
||||
&> .usa-nav__primary-item > a {
|
||||
font-size: size("body", 4);
|
||||
}
|
||||
&> .usa-nav__primary-item:last-child {
|
||||
margin-left: auto;
|
||||
@include u-margin-right(-4);
|
||||
}
|
||||
}
|
||||
|
||||
.usa-nav__primary
|
||||
|
||||
|
||||
h1 {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.sub-content {
|
||||
h1,
|
||||
h2:not(.get-started-list__heading, .usa-process-list__heading) {
|
||||
margin-top: units(4);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2:not(.get-started-list__heading, .usa-process-list__heading),
|
||||
h3 {
|
||||
margin-top: units(5);
|
||||
margin-bottom: units(2);
|
||||
}
|
||||
}
|
||||
|
||||
p,
|
||||
.list li {
|
||||
font-family: family("sans");
|
||||
@@ -64,16 +67,6 @@ p,
|
||||
font-size: size("body", 16);
|
||||
}
|
||||
|
||||
.big-number-with-status .big-number,
|
||||
.big-number-with-status .big-number-smaller {
|
||||
@include u-bg("primary");
|
||||
}
|
||||
|
||||
.big-number-with-status .big-number-status,
|
||||
.big-number-with-status .big-number-status-failing {
|
||||
@include u-bg("green-cool-40v");
|
||||
}
|
||||
|
||||
.show-more-no-border:focus,
|
||||
.show-more:focus,
|
||||
.table-field-right-aligned a:active:before,
|
||||
@@ -81,14 +74,6 @@ p,
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.govuk-checkboxes__label::after {
|
||||
top: 13px;
|
||||
left: 9px;
|
||||
width: 22px;
|
||||
height: 11px;
|
||||
}
|
||||
|
||||
.govuk-checkboxes__input:focus + .govuk-checkboxes__label::before,
|
||||
.multiple-choice [type="radio"]:focus + label::before {
|
||||
box-shadow: 0 0 0 3px color("blue-40v");
|
||||
}
|
||||
@@ -120,10 +105,6 @@ td.table-empty-message {
|
||||
font-family: family("sans");
|
||||
}
|
||||
|
||||
.govuk-inset-text {
|
||||
border-color: color("blue-40v");
|
||||
}
|
||||
|
||||
.usa-dark-background .pill-item__label {
|
||||
color: white;
|
||||
text-decoration: none !important;
|
||||
@@ -133,11 +114,6 @@ td.table-empty-message {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.sub-content .banner-dangerous h1,
|
||||
.sub-content h2.user-list-item-heading {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.user-list-edit-link:active:before,
|
||||
.user-list-edit-link:focus:before {
|
||||
box-shadow: none;
|
||||
@@ -156,32 +132,41 @@ td.table-empty-message {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.template-list-item-without-ancestors a.template-list-folder:first-of-type {
|
||||
background-image: url(../images/folder-blue-bold.svg);
|
||||
background-size: 30px 30px;
|
||||
}
|
||||
|
||||
.folder-heading a.folder-heading-folder,
|
||||
.template-list-folder {
|
||||
background-image: url(../images/folder-blue-bold.svg);
|
||||
&:hover {
|
||||
color: color("blue-warm-70v");
|
||||
background-image: url(../images/folder-blue-bold.svg);
|
||||
}
|
||||
}
|
||||
|
||||
.folder-heading-folder {
|
||||
background-image: url(../images/folder-black.svg);
|
||||
}
|
||||
|
||||
.folder-heading a.folder-heading-folder-truncated {
|
||||
background: url(../img/material-icons/folder.svg) no-repeat;
|
||||
padding-left: units(4);
|
||||
display: inline-block;
|
||||
background-position: 0;
|
||||
}
|
||||
|
||||
.usa-template-list-template {
|
||||
.usa-checkbox .usa-checkbox__label {
|
||||
+ a.template-list-folder {
|
||||
margin-left: units(4);
|
||||
}
|
||||
}
|
||||
|
||||
.usa-checkbox__label-description {
|
||||
margin: units(2px) 0 units(1) units(4);
|
||||
}
|
||||
|
||||
.folder-heading-folder {
|
||||
background: url(../img/material-icons/folder_open.svg) no-repeat;
|
||||
padding-left: units(4);
|
||||
display: inline-block;
|
||||
background-size: units(3) units(3);
|
||||
background-position: 0;
|
||||
}
|
||||
|
||||
.message-name-separator:before, .folder-heading-separator:before {
|
||||
content: ">";
|
||||
margin: 0 units(0.5);
|
||||
}
|
||||
|
||||
.template-list-template {
|
||||
background: url(../img/material-icons/description.svg) no-repeat;
|
||||
padding-left: 40px;
|
||||
background-size: 30px 30px;
|
||||
padding-left: units(4);
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.js-enabled .live-search {
|
||||
@@ -201,10 +186,8 @@ td.table-empty-message {
|
||||
}
|
||||
|
||||
.js-stick-at-bottom-when-scrolling {
|
||||
margin: 0 0 units(5);
|
||||
padding: units(2) 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
overflow: visible;
|
||||
button {
|
||||
@@ -218,8 +201,273 @@ td.table-empty-message {
|
||||
|
||||
.sticky-template-form {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 1px solid color("gray-cool-10");
|
||||
padding: units(2);
|
||||
&:focus {
|
||||
outline-offset: units(1);
|
||||
}
|
||||
}
|
||||
|
||||
.spark-bar-bar {
|
||||
font-size: units(2);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.usa-hero {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.usa-prose {
|
||||
h1, h2, h3, h4, h5 {
|
||||
font-family: family("sans");
|
||||
}
|
||||
}
|
||||
|
||||
.usa-section {
|
||||
&__home {
|
||||
h2 {
|
||||
font-family: family("serif");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-service.usa-breadcrumb {
|
||||
-bottom: 0;
|
||||
}
|
||||
|
||||
// Dashboard
|
||||
|
||||
.dashboard {
|
||||
.big-number-with-status {
|
||||
a {
|
||||
background: color("blue-60v");
|
||||
padding: units(2);
|
||||
margin-bottom: units(1);
|
||||
text-decoration: none;
|
||||
&:hover{
|
||||
background: color("blue-warm-70v");
|
||||
}
|
||||
}
|
||||
span {
|
||||
color: white;
|
||||
}
|
||||
.big-number-smaller {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.big-number-number {
|
||||
font-size: units(5);
|
||||
line-height: units(6);
|
||||
}
|
||||
.big-number-label {
|
||||
font-size: units(3);
|
||||
}
|
||||
}
|
||||
.big-number-status {
|
||||
background: color("green-cool-40v");
|
||||
display: flex;
|
||||
padding: units(1) units(2);
|
||||
&--failing {
|
||||
padding: 0;
|
||||
a.usa-link {
|
||||
color: white;
|
||||
background: color("red-warm-50v");
|
||||
padding: units(1) units(2);
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
&:hover {
|
||||
background: color("red-warm-60v");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.usa-table {
|
||||
width: 100%;
|
||||
caption {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.table-field-center-aligned {
|
||||
text-align: center;
|
||||
}
|
||||
.template-statistics-table-template-name {
|
||||
padding-left: units(4);
|
||||
display: inline-block;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url(../img/material-icons/description.svg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-table {
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
.file-list-filename {
|
||||
font-weight: bold;
|
||||
}
|
||||
.file-list-hint {
|
||||
margin: 0;
|
||||
}
|
||||
.table-field, .table-field-right-aligned {
|
||||
width: 50%;
|
||||
}
|
||||
&.usage-table {
|
||||
.table-field, .table-field-left-aligned, .table-field-right-aligned {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.usage-table {
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.big-number-smallest {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
#template-list {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
padding: units(1) 0 units(1) units(1);
|
||||
margin: units(2) 0 units(5);
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Tabs
|
||||
|
||||
.tabs {
|
||||
.pill {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
.pill-item__container {
|
||||
border: 1px solid color("gray-cool-10");
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
font-size: units(2);
|
||||
a {
|
||||
padding: units(4);
|
||||
.big-number-smaller {
|
||||
font-size: units(5);
|
||||
line-height: units(6);
|
||||
}
|
||||
.big-number-smallest {
|
||||
font-size: units(3);
|
||||
}
|
||||
&:not(.pill-item--selected):hover {
|
||||
background: color("blue-warm-70v");
|
||||
}
|
||||
&.pill-item--selected:hover {
|
||||
color: color("blue-60v");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Etc
|
||||
|
||||
.email-brand, .browse-list {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
margin-bottom: units(2);
|
||||
li {
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
details form {
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
// Textbox highlight
|
||||
|
||||
.textbox-highlight {
|
||||
|
||||
&-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-textbox {
|
||||
resize: none;
|
||||
z-index: 20;
|
||||
background: none;
|
||||
}
|
||||
|
||||
&-textbox,
|
||||
&-background,
|
||||
&-foreground,
|
||||
&-mask {
|
||||
font-size: units(2);
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
padding: 4px;
|
||||
overflow: hidden;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
&-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
color: transparent;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
border: 2px solid transparent;
|
||||
padding-bottom: units(3);
|
||||
z-index: 10;
|
||||
|
||||
// transparent borders become visible in high contrast modes so set to match background
|
||||
@media (-ms-high-contrast: active), (forced-colors: active) {
|
||||
border-color: Canvas;
|
||||
}
|
||||
|
||||
.placeholder,
|
||||
.placeholder-conditional {
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
&:after {
|
||||
content: "";
|
||||
background-color: color("yellow-20v");
|
||||
border: 1px solid color("yellow-20v");
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 5px;
|
||||
right: 6px;
|
||||
height: 100%;
|
||||
border-radius: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.textbox-help-link {
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
|
||||
.textbox-right-aligned {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.extra-tracking .usa-input {
|
||||
font-size: units(2);
|
||||
padding-left: 5px;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@forward "uswds-theme";
|
||||
@forward "uswds";
|
||||
@forward "uswds-theme-custom-styles";
|
||||
@forward "legacy-styles";
|
||||
@forward "data-visualization";
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
|
||||
.govuk-grid-column-one-sixth {
|
||||
@include govuk-grid-column(one-sixth, $class: false);
|
||||
}
|
||||
|
||||
.govuk-grid-column-five-sixths {
|
||||
@include govuk-grid-column(five-sixths, $class: false);
|
||||
}
|
||||
|
||||
.govuk-grid-column-one-eighth {
|
||||
@include govuk-grid-column(one-eighth, $class: false);
|
||||
}
|
||||
|
||||
.govuk-grid-column-five-eighths {
|
||||
@include govuk-grid-column(five-eighths, $class: false);
|
||||
}
|
||||
|
||||
.govuk-grid-column-seven-eighths {
|
||||
@include govuk-grid-column(seven-eighths, $class: false);
|
||||
}
|
||||
|
||||
%top-gutter,
|
||||
.top-gutter {
|
||||
@extend %contain-floats;
|
||||
display: block;
|
||||
margin-top: govuk-spacing(6);
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.top-gutter-4-3 {
|
||||
@extend %top-gutter;
|
||||
margin-top: govuk-spacing(7);
|
||||
}
|
||||
|
||||
.top-gutter-1-2 {
|
||||
@extend %top-gutter;
|
||||
margin-top: govuk-spacing(3);
|
||||
}
|
||||
|
||||
.top-gutter-1-3 {
|
||||
@extend %top-gutter;
|
||||
margin-top: govuk-spacing(2);
|
||||
}
|
||||
|
||||
.top-gutter-2-3 {
|
||||
@extend %top-gutter;
|
||||
margin-top: govuk-spacing(4);
|
||||
}
|
||||
|
||||
.top-gutter-0 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
%bottom-gutter,
|
||||
.bottom-gutter {
|
||||
@extend %contain-floats;
|
||||
margin-bottom: govuk-spacing(6);
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bottom-gutter-2-3 {
|
||||
@extend %bottom-gutter;
|
||||
margin-bottom: govuk-spacing(4);
|
||||
}
|
||||
|
||||
.bottom-gutter-1-2 {
|
||||
@extend %bottom-gutter;
|
||||
margin-bottom: govuk-spacing(3);
|
||||
}
|
||||
|
||||
.bottom-gutter-1-3 {
|
||||
@extend %bottom-gutter;
|
||||
margin-bottom: govuk-spacing(2);
|
||||
}
|
||||
|
||||
.bottom-gutter-3-2 {
|
||||
@extend %bottom-gutter;
|
||||
margin-bottom: govuk-spacing(6) * 3/2;
|
||||
}
|
||||
|
||||
.bottom-gutter-2 {
|
||||
@extend %bottom-gutter;
|
||||
margin-bottom: govuk-spacing(9);
|
||||
}
|
||||
|
||||
.left-gutter {
|
||||
padding-left: govuk-spacing(6);
|
||||
}
|
||||
|
||||
.left-gutter-4-3 {
|
||||
padding-left: govuk-spacing(7);
|
||||
}
|
||||
|
||||
.align-with-heading {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 13px;
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.align-with-heading-copy {
|
||||
display: block;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.align-with-heading-copy-right {
|
||||
display: block;
|
||||
margin: 21px 0 19px 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.align-with-big-number-hint {
|
||||
margin-top: govuk-spacing(8);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copy of _url-helpers.scss in govuk_frontend_toolkit
|
||||
// to prepend the path to where we store images
|
||||
|
||||
@function file-url($file) {
|
||||
$url: '';
|
||||
@if $path {
|
||||
$url: url($path + $file);
|
||||
} @else {
|
||||
$url: url($file);
|
||||
}
|
||||
@return $url;
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
// Extra CSS overlaying elements
|
||||
.form-control-1-1 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-control-5em {
|
||||
|
||||
width: 100%;
|
||||
|
||||
@include media(tablet) {
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.column-main {
|
||||
|
||||
.heading-large,
|
||||
> .heading-medium {
|
||||
margin: govuk-spacing(3) 0 govuk-spacing(4) 0;
|
||||
word-wrap: break-word;
|
||||
|
||||
&.top-gutter-0 {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.highlight {
|
||||
font-family: monospace;
|
||||
overflow-x: scroll;
|
||||
padding: 10px 0 10px 10px;
|
||||
}
|
||||
|
||||
.inline {
|
||||
|
||||
.block-label {
|
||||
|
||||
@include media(tablet) {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.heading-xlarge {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.heading-medium {
|
||||
margin-top: govuk-spacing(6);
|
||||
}
|
||||
|
||||
.form-label {
|
||||
margin-bottom: 5px;
|
||||
|
||||
&.heading-small {
|
||||
@include bold-19();
|
||||
}
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: $secondary-text-colour;
|
||||
|
||||
.form-label + & {
|
||||
display: block;
|
||||
margin-top: -5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.list-bullet {
|
||||
|
||||
.list-bullet {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
details summary {
|
||||
text-decoration: underline;
|
||||
margin-bottom: govuk-spacing(3);
|
||||
}
|
||||
|
||||
.spreadsheet {
|
||||
|
||||
margin-bottom: -1 * govuk-spacing(6);
|
||||
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
th,
|
||||
.table-field-index {
|
||||
background: $grey-4;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border: 1px solid $border-colour;
|
||||
}
|
||||
|
||||
td {
|
||||
|
||||
border-top: 0;
|
||||
// 194 is the width of the table * 1/3.5, so the overflow cuts off
|
||||
// at 3.5 columns wide.
|
||||
// 11 accounts for the padding of the table cell
|
||||
min-width: 194px - 11px;
|
||||
|
||||
&:first-child {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.fullscreen-fixed-table {
|
||||
|
||||
z-index: 1000;
|
||||
|
||||
.table-field-heading-first {
|
||||
background: $grey-4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.body-copy-table {
|
||||
|
||||
table {
|
||||
|
||||
th,
|
||||
td {
|
||||
@include core-19;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
thead {
|
||||
th {
|
||||
@include bold-19;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.tabular-numbers {
|
||||
@include core-19($tabular-numbers: true);
|
||||
}
|
||||
|
||||
summary::-moz-details-marker {
|
||||
display: none;
|
||||
}
|
||||
summary::-ms-details-marker {
|
||||
display: none;
|
||||
}
|
||||
summary::-o-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
summary::details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
details .arrow {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.block-label-hint {
|
||||
@include core-16;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.multiple-choice input:disabled+label {
|
||||
opacity: 1;
|
||||
color: $secondary-text-colour;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.heading-inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bordered-text-box {
|
||||
padding: 5px;
|
||||
outline: 2px solid $black;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.heading-upcoming-jobs {
|
||||
margin-top: govuk-spacing(3);
|
||||
}
|
||||
|
||||
@keyframes live-pulse {
|
||||
0% {
|
||||
background: $red;
|
||||
box-shadow: inset 0 0 0 2px $red, inset 0 0 0 4px $white;
|
||||
}
|
||||
40% {
|
||||
background: $red;
|
||||
box-shadow: inset 0 0 0 2px $red, inset 0 0 0 4px $white;
|
||||
}
|
||||
50% {
|
||||
background: $white;
|
||||
box-shadow: inset 0 0 0 2px $red, inset 0 0 0 2px $white;
|
||||
}
|
||||
100% {
|
||||
background: $white;
|
||||
box-shadow: inset 0 0 0 2px $red, inset 0 0 0 4px $white;
|
||||
}
|
||||
}
|
||||
|
||||
input:-webkit-autofill::first-line {
|
||||
@include core-19;
|
||||
}
|
||||
|
||||
.pilot-disabled {
|
||||
a {
|
||||
background-color: gray;
|
||||
border-color: gray;
|
||||
|
||||
span {
|
||||
color: lightgray;
|
||||
}
|
||||
}
|
||||
.big-number-status {
|
||||
background-color: gray;
|
||||
border-color: gray;
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
& > * {
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
.area-list {
|
||||
|
||||
display: inline;
|
||||
|
||||
&-item {
|
||||
|
||||
display: inline-block;
|
||||
border: 2px solid $black;
|
||||
// Create space for the remove link on the right of the list item (including borders)
|
||||
padding: (govuk-spacing(1) + 1px) (govuk-spacing(2) + 37px) govuk-spacing(1) govuk-spacing(2);
|
||||
margin: 0 govuk-spacing(1) govuk-spacing(2) 0;
|
||||
position: relative;
|
||||
|
||||
&-remove {
|
||||
|
||||
display: block; // fallback for browsers that don't support flexbox
|
||||
display: flex;
|
||||
// center SVG vertically and horizontally
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: -2px; // cover list-item border-top
|
||||
right: -2px; // cover list-item border-right
|
||||
bottom: -2px; // cover list-item border-bottom
|
||||
width: 37px;
|
||||
padding: 0;
|
||||
border: 2px solid $govuk-blue;
|
||||
border-left: none;
|
||||
text-align: center;
|
||||
font-size: inherit; // counter govuk-button styles
|
||||
line-height: 36px; // match area-list-item for browsers that don't support flexbox
|
||||
|
||||
// set styles so they don't get overriden by govuk-button styles
|
||||
&,
|
||||
&:hover {
|
||||
background: $govuk-blue;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $light-blue-25;
|
||||
}
|
||||
|
||||
// set styles so they don't get overriden by govuk-button styles
|
||||
&:active {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
// The box-shadow that separates the remove link from the list item is hidden in
|
||||
// high contrast mode. Remake using a border instead
|
||||
// Also sets text colour to match govuk-button
|
||||
@media (-ms-high-contrast: active), (forced-colors: active) {
|
||||
|
||||
&:link,
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus:not(:active):not(:hover) {
|
||||
border-left: 2px solid $govuk-blue;
|
||||
color: LinkText;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// set styles so they don't get overriden by govuk-button styles
|
||||
&,
|
||||
&:focus,
|
||||
&:focus:not(:active):not(:hover) {
|
||||
box-shadow: -2px 0 0 0 $black, inset 1px 0 0 0 rgba($white, 0.1);
|
||||
}
|
||||
|
||||
// set styles so they don't get overriden by govuk-button styles
|
||||
&:focus,
|
||||
&:focus:not(:active):not(:hover) {
|
||||
border-color: $black;
|
||||
}
|
||||
|
||||
&:focus:hover {
|
||||
background-color: $govuk-focus-colour;
|
||||
color: $black;
|
||||
}
|
||||
|
||||
// show outline to make focus visible when background colours are overridden
|
||||
@media (-ms-high-contrast: active), (forced-colors: active) {
|
||||
|
||||
// set styles so they don't get overriden by govuk-button styles
|
||||
&:focus:not(:active):not(:hover) {
|
||||
outline: 3px solid transparent;
|
||||
}
|
||||
|
||||
// override hover styles when focused, to preserve dimensions and outline
|
||||
&:focus:hover {
|
||||
top: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
width: 37px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: govuk-spacing(1) * 1.5;
|
||||
}
|
||||
|
||||
&--unremoveable {
|
||||
padding-right: govuk-spacing(2);
|
||||
background: $light-blue-25;
|
||||
color: mix($black, $govuk-blue, 66%);
|
||||
border-color: $light-blue-25;
|
||||
margin: 0 govuk-spacing(1) govuk-spacing(2) 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&--smaller {
|
||||
@include bold-16;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.govuk-button--secondary {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#area-list-map {
|
||||
z-index: 50;
|
||||
margin-bottom: govuk-spacing(4);
|
||||
}
|
||||
|
||||
.area-list-key {
|
||||
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin-right: govuk-spacing(3);
|
||||
margin-bottom: govuk-spacing(4);
|
||||
height: govuk-spacing(7);
|
||||
padding-left: govuk-spacing(8);
|
||||
line-height: govuk-spacing(7);
|
||||
|
||||
&__icon {
|
||||
position: absolute;
|
||||
left: -5px;
|
||||
top: -7px;
|
||||
}
|
||||
|
||||
&--certain {
|
||||
&:before {
|
||||
border: 2px solid $black;
|
||||
background: $light-blue-50;
|
||||
}
|
||||
}
|
||||
|
||||
&--likely {
|
||||
&:before {
|
||||
padding: 1px;
|
||||
border: 2px dashed $govuk-blue;
|
||||
border-image: file-url('dashed-border-govuk-blue.svg') 4 round;
|
||||
background: $light-blue-25;
|
||||
}
|
||||
}
|
||||
|
||||
&--phone-estimate {
|
||||
padding-left: govuk-spacing(1);
|
||||
margin-right: 0;
|
||||
float: right;
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.area-list-map-details {
|
||||
|
||||
.govuk-details__summary {
|
||||
display: block;
|
||||
padding-top: govuk-spacing(1);
|
||||
padding-bottom: govuk-spacing(1);
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.govuk-details__text {
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-bottom: 1px solid $border-colour;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
%banner,
|
||||
.banner,
|
||||
.banner-default {
|
||||
|
||||
@include bold-19;
|
||||
color: $button-colour;
|
||||
display: block;
|
||||
padding: govuk-spacing(3);
|
||||
margin: govuk-spacing(3) 0 govuk-spacing(6) 0;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
clear: both;
|
||||
border: 5px solid $button-colour;
|
||||
|
||||
&-title {
|
||||
@include bold-24;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 10px 0 5px 0;
|
||||
}
|
||||
|
||||
.list-bullet {
|
||||
@include copy-19;
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
margin-bottom: govuk-spacing(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%banner-with-tick,
|
||||
.banner-with-tick {
|
||||
padding: govuk-spacing(3) (govuk-spacing(6) + govuk-spacing(3));
|
||||
background-image: file-url('tick.png');
|
||||
background-size: 19px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: govuk-spacing(3) govuk-spacing(3);
|
||||
|
||||
}
|
||||
|
||||
.banner-default-with-tick {
|
||||
@extend %banner;
|
||||
@extend %banner-with-tick;
|
||||
}
|
||||
|
||||
.banner-dangerous {
|
||||
|
||||
@extend %banner;
|
||||
@include bold-19;
|
||||
background: $white;
|
||||
color: $text-colour;
|
||||
border: 5px solid $error-colour;
|
||||
margin: 15px 0;
|
||||
text-align: left;
|
||||
|
||||
&:focus {
|
||||
outline: 3px solid $govuk-focus-colour;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.banner-tour {
|
||||
|
||||
@extend %banner;
|
||||
background: $govuk-blue;
|
||||
color: $white;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0;
|
||||
padding: govuk-spacing(6);
|
||||
height: 425px;
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 -1em 1.6em 0 rgba(0, 0, 0, 0.05);
|
||||
border: 0;
|
||||
|
||||
&-no-fixed-height {
|
||||
height: auto;
|
||||
padding-top: govuk-spacing(8);
|
||||
min-height: 425px;
|
||||
}
|
||||
|
||||
&-with-service-name {
|
||||
margin-top: -10px;
|
||||
padding-top: 0;
|
||||
box-shadow: 0 -1px 0 0 darken($govuk-blue, 10%);
|
||||
}
|
||||
|
||||
.heading-medium {
|
||||
@include core-24;
|
||||
}
|
||||
|
||||
p {
|
||||
|
||||
margin-top: 0;
|
||||
margin-bottom: govuk-spacing(6);
|
||||
color: $white;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
& + p {
|
||||
margin-top: -1 * govuk-spacing(3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ul {
|
||||
@include core-24;
|
||||
color: $white;
|
||||
margin-bottom: govuk-spacing(5);
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: govuk-spacing(3);
|
||||
list-style-type: disc;
|
||||
margin-left: govuk-spacing(6);
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
padding: 0 ;
|
||||
margin: 0 0 govuk-spacing(6) 0;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $white;
|
||||
background-color: $link-hover-colour;
|
||||
box-shadow: 0 0 0 10px $link-hover-colour;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
color: $govuk-focus-text-colour;
|
||||
background-color: $govuk-focus-colour;
|
||||
box-shadow: 0 0 0 10px $govuk-focus-colour, 0 4px 0 10px $govuk-focus-text-colour;
|
||||
}
|
||||
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: relative;
|
||||
top: -1 * govuk-spacing(2);
|
||||
}
|
||||
|
||||
.greyed-out-step {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.banner-dashboard {
|
||||
|
||||
$baseline-shift: 5px;
|
||||
|
||||
display: block; // for browsers that don't support flexbox
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
padding: (govuk-spacing(3) - 1px) 0 (govuk-spacing(3) + 1px) 0;
|
||||
border-top: 1px solid $border-colour;
|
||||
border-bottom: 1px solid $border-colour;
|
||||
margin-bottom: govuk-spacing(6);
|
||||
text-decoration: none;
|
||||
|
||||
&:focus {
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
&-count,
|
||||
&-meta {
|
||||
float: left; // for browsers that don't support flexbox
|
||||
}
|
||||
|
||||
&-count {
|
||||
@include govuk-font(36, $weight: bold, $tabular: true);
|
||||
padding-right: 8px;
|
||||
position: relative;
|
||||
// remove the top of the extra line-height this introduces
|
||||
top: $baseline-shift;
|
||||
margin-top: -$baseline-shift;
|
||||
flex: 0 1 0.85ch;
|
||||
}
|
||||
|
||||
&-count-label {
|
||||
@include govuk-font(24, $weight: bold);
|
||||
text-decoration: underline;
|
||||
padding-right: govuk-spacing(6);
|
||||
margin: 10px 0px 5px; // 10px includes 5px extra to counter the -5px margin-top on the count item
|
||||
flex: 2 1 auto;
|
||||
}
|
||||
|
||||
&-meta {
|
||||
@include govuk-font(19);
|
||||
float: right;
|
||||
text-align: right;
|
||||
flex: initial;
|
||||
}
|
||||
|
||||
& + .banner-dashboard {
|
||||
margin-top: -1 * govuk-spacing(6);
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
%big-number,
|
||||
.big-number {
|
||||
|
||||
display: block;
|
||||
|
||||
&-number,
|
||||
&-label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&-number {
|
||||
@include bold-48($tabular-numbers: true);
|
||||
}
|
||||
|
||||
&-label {
|
||||
@include core-19;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.pill .big-number-number {
|
||||
|
||||
// reduce padding until screen is bigger than 420px / zoomed below 300%
|
||||
padding-left: govuk-spacing(1);
|
||||
|
||||
@include govuk-media-query($from: 420px) {
|
||||
padding-left: govuk-spacing(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number-dark {
|
||||
@extend %big-number;
|
||||
padding: govuk-spacing(3);
|
||||
position: relative;
|
||||
background: $black;
|
||||
color: $white;
|
||||
|
||||
.big-number-number {
|
||||
@include bold-36($tabular-numbers: true);
|
||||
}
|
||||
}
|
||||
|
||||
.big-number-smaller {
|
||||
|
||||
@extend %big-number;
|
||||
|
||||
.big-number-number {
|
||||
@include bold-36($tabular-numbers: true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number-smallest {
|
||||
|
||||
@extend %big-number;
|
||||
|
||||
.big-number-number {
|
||||
@include bold-24($tabular-numbers: true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number-with-status {
|
||||
|
||||
@extend %big-number;
|
||||
position: relative;
|
||||
margin-bottom: govuk-spacing(4);
|
||||
|
||||
.big-number,
|
||||
.big-number-smaller {
|
||||
padding: govuk-spacing(3);
|
||||
position: relative;
|
||||
background: $black;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.big-number-label {
|
||||
|
||||
padding-bottom: 0;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: $link-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number-link {
|
||||
|
||||
text-decoration: none;
|
||||
background: $link-colour;
|
||||
color: $white;
|
||||
display: block;
|
||||
border: 2px solid $link-colour;
|
||||
margin-bottom: 5px;
|
||||
|
||||
&:hover {
|
||||
|
||||
color: $light-blue-25;
|
||||
|
||||
.big-number,
|
||||
.big-number-number,
|
||||
.big-number-smaller,
|
||||
.big-number-label {
|
||||
color: $light-blue-25;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
background: $govuk-focus-colour;
|
||||
border: none;
|
||||
padding: 2px; /* replace the spacing the border gave */
|
||||
/* override the default focus style to inset the underline */
|
||||
box-shadow: inset 0 -4px $govuk-focus-text-colour;
|
||||
|
||||
.big-number-number,
|
||||
.big-number-smaller,
|
||||
.big-number-label {
|
||||
color: $govuk-focus-text-colour;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.big-number,
|
||||
.big-number-smaller {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.big-number-label {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%big-number-status,
|
||||
.big-number-status {
|
||||
|
||||
@include core-19;
|
||||
display: block;
|
||||
background: $green;
|
||||
color: $white;
|
||||
padding: 15px;
|
||||
|
||||
a {
|
||||
|
||||
&:link,
|
||||
&:visited,
|
||||
&:active,
|
||||
&:hover {
|
||||
color: $white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number-status-failing {
|
||||
@extend %big-number-status;
|
||||
background: $error-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number-meta-wrapper {
|
||||
|
||||
position: relative;
|
||||
margin: govuk-spacing(3) 0 govuk-spacing(6) 0;
|
||||
background: $govuk-blue;
|
||||
|
||||
.big-number-meta {
|
||||
|
||||
padding: govuk-spacing(2) govuk-spacing(3);
|
||||
color: $white;
|
||||
pointer-events: none;
|
||||
|
||||
@include media(desktop) {
|
||||
position: absolute;
|
||||
bottom: 7px;
|
||||
right: 5px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
.browse-list {
|
||||
margin-bottom: govuk-spacing(3);
|
||||
|
||||
.browse-sub-list {
|
||||
margin-top: govuk-spacing(3);
|
||||
margin-left: govuk-spacing(6);
|
||||
@include media('desktop') {
|
||||
margin-left: govuk-spacing(9);
|
||||
}
|
||||
}
|
||||
|
||||
&-item,
|
||||
&-sub-item {
|
||||
@include bold-24;
|
||||
list-style: none;
|
||||
margin-bottom: govuk-spacing(3);
|
||||
}
|
||||
|
||||
&-link {
|
||||
@include govuk-font(24, $weight: bold);
|
||||
}
|
||||
|
||||
&-hint {
|
||||
@include core-19;
|
||||
margin: 5px 0 10px 0;
|
||||
color: $secondary-text-colour;
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
// Taken from https://github.com/alphagov/govuk-frontend/blob/v2.13.0/src/components/checkboxes/_checkboxes.scss
|
||||
$govuk-touch-target-size: 44px;
|
||||
$govuk-checkboxes-size: 40px;
|
||||
|
||||
.selection-summary {
|
||||
|
||||
.selection-summary__text {
|
||||
@include core-19($tabular-numbers: true);
|
||||
padding: 5px 0 0 0;
|
||||
margin-bottom: govuk-spacing(3);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.selection-summary__text--folders {
|
||||
padding: 10px 15px 5px 51px;
|
||||
background-image: file-url('folder-black.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 39px auto;
|
||||
background-position: 0px 4px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.checkboxes-nested {
|
||||
|
||||
margin-bottom: 10px;
|
||||
|
||||
.multiple-choice {
|
||||
|
||||
$circle-diameter: 39px;
|
||||
$border-thickness: 4px;
|
||||
$border-indent: ($circle-diameter / 2) - ($border-thickness / 2);
|
||||
$border-colour: $border-colour;
|
||||
|
||||
float: none;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: $border-indent;
|
||||
width: $border-thickness;
|
||||
height: 100%;
|
||||
background: $border-colour;
|
||||
}
|
||||
|
||||
label {
|
||||
float: none;
|
||||
}
|
||||
|
||||
[type=checkbox]+label::before {
|
||||
// To overlap the grey inset line
|
||||
background: $white;
|
||||
}
|
||||
|
||||
ul {
|
||||
// To equalise the spacing between the line and the top/bottom of
|
||||
// the radio
|
||||
margin-top: 5px;
|
||||
margin-bottom: -5px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.govuk-form-group--nested {
|
||||
|
||||
$border-thickness: $govuk-touch-target-size - $govuk-checkboxes-size;
|
||||
$border-indent: $govuk-touch-target-size / 2;
|
||||
|
||||
position: relative;
|
||||
|
||||
// To equalise the spacing between the line and the top/bottom of
|
||||
// the radio
|
||||
margin-top: govuk-spacing(1) + ($border-thickness / 2);
|
||||
margin-bottom: govuk-spacing(1) * -1;
|
||||
padding-left: govuk-spacing(2) + 2;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: $border-indent * -1;
|
||||
width: $border-thickness;
|
||||
height: 100%;
|
||||
background: $govuk-border-colour;
|
||||
}
|
||||
}
|
||||
|
||||
.selection-content {
|
||||
margin-bottom: govuk-spacing(4);
|
||||
|
||||
.checkboxes-nested {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.selection-footer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.selection-footer__button {
|
||||
// revert full-width button for smaller screens
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// styles specific to the collapsible checkboxes module
|
||||
.selection-wrapper {
|
||||
fieldset:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
$border-thickness: 4px;
|
||||
$top-spacing: govuk-spacing(5);
|
||||
|
||||
.multiple-choice {
|
||||
|
||||
z-index: 10;
|
||||
|
||||
.block-label {
|
||||
&:before {
|
||||
box-shadow: 0 5px 0 0 $white;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.conditional-radios {
|
||||
|
||||
&-panel {
|
||||
border-left: $border-thickness solid $border-colour;
|
||||
margin: 0 0 (-1 * govuk-spacing(3)) (govuk-spacing(3) + ($border-thickness / 2));
|
||||
padding: govuk-spacing(3) 0 0 (govuk-spacing(6) - ($border-thickness / 2));
|
||||
position: relative;
|
||||
top: -$top-spacing;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
.content-metadata {
|
||||
@include govuk-font(16);
|
||||
@include govuk-clearfix;
|
||||
@include govuk-responsive-margin(5, "bottom");
|
||||
color: $govuk-text-colour;
|
||||
margin-top: 15px;
|
||||
|
||||
.content-metadata__term,
|
||||
.content-metadata__definition {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.content-metadata__term {
|
||||
margin-top: .5em;
|
||||
|
||||
@include govuk-media-query($from: tablet) {
|
||||
box-sizing: border-box;
|
||||
float: left;
|
||||
clear: left;
|
||||
padding-right: govuk-spacing(1);
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content-metadata__definition {
|
||||
margin: 0;
|
||||
|
||||
@include govuk-media-query($from: tablet) {
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: govuk-spacing(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-metadata__suffix {
|
||||
margin-top: 0.628em;
|
||||
margin-bottom: 0;
|
||||
|
||||
@include govuk-media-query($from: tablet) {
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
.copy-to-clipboard {
|
||||
|
||||
position: relative;
|
||||
padding-bottom: 38px; // height of button
|
||||
display: flex;
|
||||
|
||||
&__name {
|
||||
@include bold-19;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
&__notice,
|
||||
&__value {
|
||||
font-family: monospace;
|
||||
display: block;
|
||||
padding: 0 0 10px 0;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
&__button--show,
|
||||
&__button--copy {
|
||||
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
left: 0px;
|
||||
|
||||
&:active {
|
||||
top: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
$white-50-opaque: rgba($white, 0.5);
|
||||
$button-bottom-border-colour: rgba(0, 0, 0, 0.17);
|
||||
$email-message-gutter: govuk-spacing(9);
|
||||
|
||||
// sass-lint:disable no-important
|
||||
|
||||
.email-message {
|
||||
|
||||
margin-bottom: govuk-spacing(6);
|
||||
border: 1px solid $border-colour;
|
||||
|
||||
&-meta {
|
||||
|
||||
@include core-19;
|
||||
margin: 0;
|
||||
|
||||
td,
|
||||
th {
|
||||
@include core-19;
|
||||
border-top: 0;
|
||||
border-bottom: 1px solid $border-colour;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
th {
|
||||
color: $secondary-text-colour;
|
||||
padding-left: $email-message-gutter;
|
||||
}
|
||||
|
||||
td {
|
||||
|
||||
width: 99%;
|
||||
padding-right: $email-message-gutter;
|
||||
word-break: break-word;
|
||||
|
||||
&:last-child {
|
||||
padding-right: $email-message-gutter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-from {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
&-body {
|
||||
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: govuk-spacing(3) $email-message-gutter 0 $email-message-gutter;
|
||||
margin: 0 0 0 0;
|
||||
clear: both;
|
||||
position: relative;
|
||||
word-wrap: break-word;
|
||||
|
||||
table {
|
||||
margin: 0 0 20px 0;
|
||||
|
||||
td {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
font-family: inherit !important;
|
||||
}
|
||||
|
||||
li:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
.js-enabled {
|
||||
.file-upload {
|
||||
&-label {
|
||||
@include bold-19;
|
||||
display: block;
|
||||
margin: 0 0 10px 0;
|
||||
|
||||
.error-message {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-field {
|
||||
width: 0.1px;
|
||||
height: 0.1px;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
|
||||
// &:focus + .file-upload-button {
|
||||
// background: $govuk-focus-colour;
|
||||
// color: $govuk-focus-text-colour;
|
||||
// }
|
||||
}
|
||||
|
||||
&-filename {
|
||||
@include bold-19;
|
||||
display: inline-block;
|
||||
padding-left: govuk-spacing(3);
|
||||
}
|
||||
|
||||
&-submit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-alternate-link {
|
||||
display: inline-block;
|
||||
line-height: 35px;
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
.fullscreen {
|
||||
|
||||
&-content {
|
||||
|
||||
background: $white;
|
||||
z-index: 10;
|
||||
overflow-y: hidden;
|
||||
box-sizing: border-box;
|
||||
margin: 0 0 govuk-spacing(6) 0;
|
||||
padding: 0 0 0 0;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid $border-colour;
|
||||
|
||||
.table {
|
||||
|
||||
margin-bottom: 0;
|
||||
|
||||
tr:last-child {
|
||||
td {
|
||||
border-bottom: 1px solid $white;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
th,
|
||||
.table-field-error-label,
|
||||
.table-field-left-aligned {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-right-shadow {
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
z-index: 200;
|
||||
|
||||
&.visible {
|
||||
|
||||
&.with-transition {
|
||||
transition: box-shadow 0.6s ease-out;
|
||||
}
|
||||
|
||||
box-shadow: inset -1px 0 0 0 $border-colour, inset -3px 0 0 0 rgba($border-colour, 0.2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-scrollable-table {
|
||||
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
.table-field-heading-first,
|
||||
.table-field-index {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-field-left-aligned {
|
||||
position: relative;
|
||||
z-index: 150;
|
||||
background: $white;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar:horizontal {
|
||||
height: 11px;
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 8px;
|
||||
border: 2px solid $white;
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: $white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-fixed-table {
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
|
||||
.table-field-heading {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.table-field-left-aligned {
|
||||
width: 0;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.table-field-heading-first,
|
||||
.table-field-index {
|
||||
transition: none;
|
||||
position: relative;
|
||||
z-index: 200;
|
||||
background: $white;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-scrolled-table {
|
||||
|
||||
padding-bottom: 20px;
|
||||
|
||||
.table-field-heading-first,
|
||||
.table-field-index {
|
||||
transition: box-shadow 0.3s ease-in-out;
|
||||
box-shadow: 1px 0 0 0 $border-colour, 3px 0 0 0 rgba($border-colour, 0.2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-shim {
|
||||
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 9;
|
||||
margin-bottom: 30px;
|
||||
pointer-events: none;
|
||||
|
||||
& + .table-show-more-link {
|
||||
margin-top: -28px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.js-focus-style {
|
||||
outline: 3px solid $govuk-focus-text-colour;
|
||||
box-shadow: 0 0 0 7px $govuk-focus-colour;
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
.input-list {
|
||||
|
||||
.list-entry {
|
||||
vertical-align: middle;
|
||||
margin-bottom: 15px;
|
||||
position: relative;
|
||||
|
||||
// move the left border, signifying the error, out of the main column
|
||||
.govuk-form-group--error {
|
||||
margin-left: ($govuk-border-width + $govuk-gutter-half) * -1;
|
||||
}
|
||||
}
|
||||
|
||||
&__button--remove,
|
||||
&__button--add {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&__button--remove {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
margin-top: 5px;
|
||||
position: static;
|
||||
overflow: hidden;
|
||||
|
||||
@include media(tablet) {
|
||||
@include inline-block;
|
||||
margin: 0 0 0 10px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__button--add {
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
input[type=search] {
|
||||
// overrides this nasty global from GOV.UK template
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.live-search {
|
||||
|
||||
display: none;
|
||||
|
||||
.js-enabled & {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: govuk-spacing(4);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
@keyframes ellipsis {
|
||||
to {
|
||||
width: 1.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-indicator {
|
||||
&:after {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
animation: ellipsis steps(4,end) 1.5s infinite;
|
||||
content: "\2026"; // ellipsis
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
$app-body-text-line-height-default: get-govuk-typography-style($size: 19, $breakpoint: null, $property: "line-height");
|
||||
$app-body-text-line-height-tablet: get-govuk-typography-style($size: 19, $breakpoint: tablet, $property: "line-height");
|
||||
|
||||
@mixin separator {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 20px;
|
||||
height: govuk-spacing(6);
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
bottom: 1px;
|
||||
right: 7px;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
margin: auto 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
border: solid;
|
||||
border-width: 2px 2px 0 0;
|
||||
border-color: $secondary-text-colour;
|
||||
}
|
||||
}
|
||||
|
||||
$govuk-checkboxes-size: 40px;
|
||||
$govuk-checkboxes-label-padding-left-right: govuk-spacing(3);
|
||||
$message-type-bottom-spacing: govuk-spacing(4);
|
||||
|
||||
a {
|
||||
|
||||
&:hover .message-name-separator:before {
|
||||
border-color: $link-hover-colour;
|
||||
}
|
||||
|
||||
.message-name-separator {
|
||||
|
||||
margin-right: -2px;
|
||||
margin-left: -2px;
|
||||
|
||||
&:before {
|
||||
border-color: $link-colour;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.message-name-separator {
|
||||
@include separator;
|
||||
}
|
||||
|
||||
.template-list {
|
||||
|
||||
&-item {
|
||||
|
||||
&-with-checkbox {
|
||||
padding-left: $govuk-checkboxes-size + $govuk-checkboxes-label-padding-left-right;
|
||||
}
|
||||
|
||||
&-hidden-by-default {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-without-ancestors {
|
||||
|
||||
a {
|
||||
|
||||
display: block;
|
||||
|
||||
&:first-child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.template-list-folder:first-of-type {
|
||||
background-position: 0 2px;
|
||||
padding-left: 0;
|
||||
text-indent: 35px;
|
||||
|
||||
@include govuk-media-query($from: tablet) {
|
||||
text-indent: 40px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.template-list-template,
|
||||
.template-list-folder {
|
||||
|
||||
position: relative; /* contain absolutely positioned ::before pseudo element*/
|
||||
display: block; /* fill horizontal space to allow hint/meta below to float */
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: -1 * $app-body-text-line-height-default; /* extend link by line height of hint/meta so it covers it */
|
||||
width: 100%;
|
||||
height: $app-body-text-line-height-default;
|
||||
|
||||
@include govuk-media-query($from: tablet) {
|
||||
bottom: -1 * $app-body-text-line-height-tablet;
|
||||
height: $app-body-text-line-height-tablet;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
|
||||
&,
|
||||
&::before {
|
||||
background-color: $govuk-focus-colour;
|
||||
}
|
||||
|
||||
&,
|
||||
& + .template-list-item-hint,
|
||||
.message-type {
|
||||
color: $govuk-focus-text-colour;
|
||||
}
|
||||
|
||||
&::before {
|
||||
box-shadow: 0px -2px $govuk-focus-colour, 0px 4px $govuk-focus-text-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
& + .template-list-item-hint,
|
||||
& + .message-type {
|
||||
position: relative; /* needs to be non-static to have a z-index above the link :before element */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: $govuk-checkboxes-size + $govuk-checkboxes-label-padding-left-right;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// Fix for GOVUK Frontend selector with high precendence
|
||||
// https://github.com/alphagov/govuk-frontend/blob/v2.13.0/src/components/hint/_hint.scss
|
||||
&-label:not(.govuk-label--m):not(.govuk-label--l):not(.govuk-label--xl)+.template-list-item-hint {
|
||||
margin-bottom: $message-type-bottom-spacing;
|
||||
}
|
||||
|
||||
&-hint {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-folder,
|
||||
&-template {
|
||||
@include govuk-font($size: 24, $weight: bold, $line-height: 1.5);
|
||||
@include govuk-media-query($from: tablet) {
|
||||
line-height: 1.25;
|
||||
}
|
||||
}
|
||||
|
||||
&-folder {
|
||||
|
||||
display: inline;
|
||||
padding-left: 35px;
|
||||
background-image: file-url('folder-blue-bold.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto 20px;
|
||||
background-position: 0px 4px;
|
||||
|
||||
@include govuk-media-query($from: tablet) {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-image: file-url('folder-blue-bold-hover.svg');
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
background-image: file-url('folder-black-bold.svg');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-template {
|
||||
a {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
&-empty {
|
||||
color: $secondary-text-colour;
|
||||
padding: govuk-spacing(3) 0 govuk-spacing(2) 0;
|
||||
}
|
||||
|
||||
&-selected-counter {
|
||||
color: $secondary-text-colour;
|
||||
margin: govuk-spacing(3) 0;
|
||||
|
||||
@include media(tablet) {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: govuk-spacing(6) - 1px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.content-fixed__bottom & {
|
||||
right: govuk-spacing(3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sticky-template-form {
|
||||
|
||||
padding: govuk-spacing(3);
|
||||
margin: govuk-spacing(3) * -1;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.folder-heading {
|
||||
|
||||
.govuk-grid-row & {
|
||||
margin: govuk-spacing(3) 0 20px 0;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
a,
|
||||
&-folder,
|
||||
&-subfolder {
|
||||
|
||||
display: inline;
|
||||
vertical-align: top;
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto 20px;
|
||||
background-position: 0px 4px;
|
||||
min-height: 30px;
|
||||
|
||||
}
|
||||
|
||||
&-folder {
|
||||
padding: 0 0 0 40px;
|
||||
background-image: file-url('folder-black-bold.svg');
|
||||
}
|
||||
|
||||
&-folder-truncated {
|
||||
width: 0;
|
||||
padding: 0 0 0 30px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-folder-root-truncated {
|
||||
max-width: 1.4em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
&.folder-heading-folder {
|
||||
|
||||
display: inline;
|
||||
background-image: file-url('folder-blue-bold.svg');
|
||||
background-position: 0px 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:hover {
|
||||
background-image: file-url('folder-blue-bold-hover.svg');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.folder-heading-folder-truncated {
|
||||
display: inline-block;
|
||||
background-position: 0px 2px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $link-hover-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-separator {
|
||||
@include separator;
|
||||
}
|
||||
|
||||
&-manage-link {
|
||||
display: block;
|
||||
text-align: right;
|
||||
padding: govuk-spacing(4) 0 0 0;
|
||||
position: relative;
|
||||
top: -6px;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
.navigation {
|
||||
|
||||
@include govuk-font($size: 19);
|
||||
padding: 0 govuk-spacing(6) 0 0;
|
||||
|
||||
$padding-top: 14px;
|
||||
$padding-bottom: 11px;
|
||||
|
||||
&-service-name,
|
||||
&-organization-link {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
// aligning to the baseline with overflow: hidden adds to the parent's height
|
||||
// aligning to the top doesn't
|
||||
// see: https://stackoverflow.com/questions/23529369/why-does-x-overflowhidden-cause-extra-space-below#answer-51088033
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&-service-type {
|
||||
|
||||
@include bold-16;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-left: govuk-spacing(2);
|
||||
padding: 0 govuk-spacing(1);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
|
||||
&--training {
|
||||
background: $grey-3;
|
||||
color: mix($grey-1, $text-colour);
|
||||
box-shadow: 0 -3px 0 0 $grey-3;
|
||||
}
|
||||
|
||||
&--suspended {
|
||||
background: $grey-3;
|
||||
color: mix($grey-1, $text-colour);
|
||||
box-shadow: 0 -3px 0 0 $grey-3;
|
||||
}
|
||||
|
||||
&--live,
|
||||
&--test,
|
||||
&--operator {
|
||||
// This uses new Design System colours to match .govuk-tag--red
|
||||
background: #F6D7D2;
|
||||
color: #942514;
|
||||
box-shadow: 0 -3px 0 0 #F6D7D2;
|
||||
}
|
||||
|
||||
&--government {
|
||||
background: #942514;
|
||||
color: #F6D7D2;
|
||||
box-shadow: 0 -3px 0 0 #942514;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-service-switch,
|
||||
&-service-back-to,
|
||||
&-organization-link {
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
text-decoration: none; // override the :hover style (the focus style has its own underline)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-service {
|
||||
|
||||
@include govuk-font($size: 19);
|
||||
border-bottom: 1px solid $border-colour;
|
||||
margin: 0 0 10px;
|
||||
position: relative;
|
||||
|
||||
&-name {
|
||||
|
||||
padding: $padding-top 0 $padding-bottom 0;
|
||||
max-width: 50%;
|
||||
|
||||
}
|
||||
|
||||
&-switch {
|
||||
|
||||
text-align: right;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: $padding-top 0 $padding-bottom govuk-spacing(3);
|
||||
|
||||
&:focus {
|
||||
padding: $padding-top 0px $padding-bottom + 1px govuk-spacing(3) + 10;
|
||||
/* override default focus style to inset bottom underline */
|
||||
box-shadow: inset 0 -4px $govuk-focus-text-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-back-to {
|
||||
|
||||
padding: $padding-top govuk-spacing(3) $padding-bottom 0;
|
||||
display: inline-block;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-organization-link {
|
||||
|
||||
max-width: 25%;
|
||||
padding: $padding-top 20px $padding-bottom 0;
|
||||
margin-right: 5px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
bottom: 1px;
|
||||
right: 2px;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
margin: auto 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
border: solid;
|
||||
border-width: 1px 1px 0 0;
|
||||
border-color: $secondary-text-colour;
|
||||
}
|
||||
|
||||
&:focus:before {
|
||||
border-color: $govuk-focus-text-colour;
|
||||
}
|
||||
|
||||
// hack to make the focus style fit in the navigation bar
|
||||
&:focus {
|
||||
box-shadow: inset 0 -3px $govuk-focus-text-colour, 0 1px $govuk-focus-text-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
display: block;
|
||||
padding: 5px 0;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
// override the :hover style (the focus style has its own underline)
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
@include bold-19;
|
||||
position: relative;
|
||||
// These two lines stop the width of the item jumping so much
|
||||
// between selected and unselected states
|
||||
left: -0.5px;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// https://github.com/alphagov/product-page-example/blob/master/source/stylesheets/modules/_sub-navigation.scss
|
||||
.sub-navigation {
|
||||
@include media(tablet) {
|
||||
margin-top: govuk-spacing(6) * 1.5;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
ol,
|
||||
ul {
|
||||
margin-top: govuk-spacing(2);
|
||||
border-top: 1px $grey-3 solid;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
@include core-16;
|
||||
|
||||
border-bottom: 1px $grey-3 solid;
|
||||
display: block;
|
||||
padding: govuk-spacing(2) 0;
|
||||
|
||||
a:link {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
ol ol & {
|
||||
padding-left: govuk-spacing(6);
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__item--active {
|
||||
@include bold-16;
|
||||
|
||||
a:link,
|
||||
a:visited {
|
||||
color: $text-colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
.page-footer {
|
||||
|
||||
position: relative;
|
||||
margin-bottom: 30px;
|
||||
|
||||
&-link {
|
||||
|
||||
line-height: 40px;
|
||||
padding: 1px 0 0 15px;
|
||||
font-weight: normal;
|
||||
|
||||
}
|
||||
|
||||
&-delete-link-without-button {
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-secondary-link {
|
||||
display: block;
|
||||
margin-top: govuk-spacing(6);
|
||||
}
|
||||
|
||||
&-right-aligned-link {
|
||||
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 9px; // align baseline with buttons
|
||||
|
||||
&-without-button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.js-cancel {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__button--centred {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.search-form__button {
|
||||
@include media(desktop) {
|
||||
position: relative;
|
||||
top: 30px;
|
||||
left: -30px;
|
||||
width: 100%;
|
||||
margin-right: -30px;
|
||||
padding-top: 9px;
|
||||
box-sizing: content-box;
|
||||
|
||||
&:active {
|
||||
top: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
.pill {
|
||||
|
||||
display: flex;
|
||||
|
||||
&-item__container {
|
||||
width: 25%;
|
||||
flex-grow: 1;
|
||||
text-align: left;
|
||||
|
||||
/*
|
||||
For browsers that don’t support flexbox, use float instead.
|
||||
Float does not create floating of flex item, and do not take it
|
||||
out-of-flow. So this is ignored by browsers that support flexbox.
|
||||
See: https://www.w3.org/TR/css-flexbox-1/#flex-containers
|
||||
*/
|
||||
float: left;
|
||||
/*
|
||||
Setting this as a flex container means the contents (1 item)
|
||||
will fill the vertical space due to `align-items` defaulting
|
||||
to `stretch`.
|
||||
See: https://css-tricks.com/snippets/css/a-guide-to-flexbox/#prop-align-items
|
||||
*/
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&-item,
|
||||
&-item--selected {
|
||||
display: flex; // float causes display: block in browsers without flexbox
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
float: left;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
&-item {
|
||||
$background: $link-colour;
|
||||
background: $background;
|
||||
color: $white;
|
||||
border: 2px solid $background;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $light-blue-25;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
z-index: 10;
|
||||
color: $govuk-focus-text-colour;
|
||||
/* override default focus styles to inset bottom underline */
|
||||
box-shadow: inset 0 -4px $govuk-focus-text-colour;
|
||||
border: none;
|
||||
padding: 12px 2px; /* compensate for lack of border with padding */
|
||||
}
|
||||
}
|
||||
|
||||
&-item--selected {
|
||||
background: inherit;
|
||||
border: 2px solid $black;
|
||||
outline: 1px solid rgba($white, 0.1);
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: $govuk-text-colour;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: govuk-tint($govuk-text-colour, 25%);
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
z-index: 1000;
|
||||
color: $govuk-focus-text-colour;
|
||||
border: solid 2px $black;
|
||||
padding: 10px 0px; /* reset padding to default */
|
||||
box-shadow: inset 0 -2px $govuk-focus-text-colour; /* remove bottom border from underline */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-item__label {
|
||||
|
||||
text-decoration: underline;
|
||||
// reduce padding until screen is above 420px / zoomed below 300%
|
||||
padding-left: govuk-spacing(1);
|
||||
|
||||
@include govuk-media-query($from: 420px) {
|
||||
padding-left: govuk-spacing(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-item,
|
||||
&-item--selected {
|
||||
&:focus .pill-item__label {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-item--centered {
|
||||
text-align: center;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.pill-separate {
|
||||
|
||||
&-item {
|
||||
|
||||
display: block;
|
||||
text-align: left;
|
||||
padding: 9px (govuk-spacing(3) - 1);
|
||||
border: 1px solid transparent;
|
||||
text-align: center;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
background: $link-colour;
|
||||
color: $white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $light-blue-25;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:link:focus {
|
||||
color: $govuk-focus-text-colour;
|
||||
text-decoration: none;
|
||||
background: $govuk-focus-colour;
|
||||
/* override default focus style to inset bottom underline */
|
||||
box-shadow: inset 0 -4px $govuk-focus-text-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
%placeholder,
|
||||
.placeholder {
|
||||
|
||||
display: inline;
|
||||
background: $govuk-focus-colour;
|
||||
color: $text-colour;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
border-radius: 20px;
|
||||
box-shadow: inset 0.47em 0 0 0 $white, inset -0.47em 0 0 0 $white, inset 0 -0.15em 0 0 $white, inset 0 0.15em 0 0 $white;
|
||||
|
||||
.sms-message-wrapper & {
|
||||
box-shadow: inset 0.47em 0 0 0 $panel-colour, inset -0.47em 0 0 0 $panel-colour, inset 0 -0.18em 0 0 $panel-colour, inset 0 0.18em 0 0 $panel-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.placeholder-no-brackets {
|
||||
@extend %placeholder;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
border-radius: 1px;
|
||||
box-shadow: inset 0 -0.1em 0 0 $white, inset 0 0.1em 0 0 $white;
|
||||
}
|
||||
|
||||
.placeholder-conditional {
|
||||
|
||||
@extend %placeholder;
|
||||
border-radius: 0;
|
||||
border-top-left-radius: 20px;
|
||||
border-bottom-left-radius: 20px;
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
box-shadow: inset 0.47em 0 0 0 $white, inset -0.89em 0 0 0 $white, inset 0 -0.16em 0 0 $white, inset 0 0.16em 0 0 $white;
|
||||
|
||||
.sms-message-wrapper & {
|
||||
box-shadow: inset 0.47em 0 0 0 $panel-colour, inset -0.89em 0 0 0 $panel-colour, inset 0 -0.18em 0 0 $panel-colour, inset 0 0.18em 0 0 $panel-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.placeholder-redacted {
|
||||
|
||||
background: currentColor;
|
||||
padding: 0 0.5em;
|
||||
opacity: 0.8;
|
||||
box-shadow: inset 0 -0.35em 0 0 $white;
|
||||
position: relative;
|
||||
top: 0.1em;
|
||||
|
||||
.sms-message-wrapper & {
|
||||
box-shadow: inset 0 -0.35em 0 0 $panel-colour;
|
||||
}
|
||||
|
||||
*:focus + p & {
|
||||
box-shadow: inset 0 -0.35em 0 0 $govuk-focus-colour;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
.branding-preview {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: solid 1px $border-colour;
|
||||
min-height: 200px;
|
||||
margin-bottom: govuk-spacing(6)
|
||||
}
|
||||
|
||||
#logo-img {
|
||||
background-color: $grey-4;
|
||||
background-image: linear-gradient(45deg, $grey-3 25%, transparent 25%), linear-gradient(-45deg, $grey-3 25%, transparent 25%), linear-gradient(45deg, transparent 75%, $grey-3 75%), linear-gradient(-45deg, transparent 75%, $grey-3 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
.radio-select {
|
||||
|
||||
min-height: 39px;
|
||||
|
||||
&__column {
|
||||
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
.multiple-choice {
|
||||
padding-right: 10px;
|
||||
padding-left: 54px - 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__button--category {
|
||||
|
||||
margin-right: govuk-spacing(3);
|
||||
width: auto;
|
||||
|
||||
}
|
||||
|
||||
&__button--done {
|
||||
|
||||
display: block;
|
||||
clear: both;
|
||||
margin: 0 0 govuk-spacing(6) 0;
|
||||
position: relative;
|
||||
top: govuk-spacing(3);
|
||||
|
||||
&:active {
|
||||
top: govuk-spacing(3) + 2px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.js-enabled & {
|
||||
|
||||
overflow: visible;
|
||||
|
||||
.multiple-choice {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.js-multiple-choice {
|
||||
display: block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.radios-nested {
|
||||
|
||||
margin-bottom: 10px;
|
||||
|
||||
.multiple-choice {
|
||||
|
||||
$circle-diameter: 39px;
|
||||
$border-thickness: 4px;
|
||||
$border-indent: ($circle-diameter / 2) - ($border-thickness / 2);
|
||||
$border-colour: $border-colour;
|
||||
|
||||
float: none;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: $border-indent;
|
||||
width: $border-thickness;
|
||||
height: 100%;
|
||||
background: $border-colour;
|
||||
}
|
||||
|
||||
label {
|
||||
float: none;
|
||||
}
|
||||
|
||||
[type=radio]+label::before {
|
||||
// To overlap the grey inset line
|
||||
background: $white;
|
||||
}
|
||||
|
||||
ul {
|
||||
// To equalise the spacing between the line and the top/bottom of
|
||||
// the radio
|
||||
margin-top: 5px;
|
||||
margin-bottom: -5px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.block-label-hint {
|
||||
&:after {
|
||||
// Adds an little extra segment of line alongside the ‘current folder’
|
||||
// hint so that it extends all the way down to the next radio
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: $circle-diameter + 5px;
|
||||
left: $border-indent;
|
||||
width: $border-thickness;
|
||||
height: 25px;
|
||||
background: $border-colour;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.inline {
|
||||
.multiple-choice {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-slider {
|
||||
|
||||
border-bottom: 2px solid $black;
|
||||
height: 18px;
|
||||
margin-bottom: 18px + 30px;
|
||||
margin-right: -18px;
|
||||
margin-left: -18px;
|
||||
white-space: nowrap;
|
||||
|
||||
legend {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.govuk-radios__item {
|
||||
|
||||
margin-right: 0;
|
||||
padding: 0;
|
||||
width: 38px;
|
||||
margin-right: calc((100% - (38px * 11)) / 10);
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
font-size: 0;
|
||||
padding: 0;
|
||||
|
||||
&:before {
|
||||
background: $white;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-left-value {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&-right-value {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
right: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
.research-mode {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
background: $govuk-blue;
|
||||
color: $white;
|
||||
border-radius: 2px;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
%show-more,
|
||||
.show-more {
|
||||
|
||||
@include core-16;
|
||||
display: block;
|
||||
padding: 0 0;
|
||||
margin: govuk-spacing(3) 0 govuk-spacing(3) 0;
|
||||
text-align: center;
|
||||
border-top: 1px solid $border-colour;
|
||||
|
||||
&:focus {
|
||||
|
||||
border-color: transparent;
|
||||
/* override default focus style to increase top yellow box-shadow */
|
||||
box-shadow: 0 -15px $govuk-focus-colour, 0 4px $govuk-focus-text-colour;
|
||||
|
||||
span {
|
||||
outline: none;
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
span {
|
||||
position: relative;
|
||||
top: -11px;
|
||||
outline: 10px solid white;
|
||||
background: $white;
|
||||
display: inline-block;
|
||||
border-bottom: 1px solid $light-blue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.show-more-no-border {
|
||||
@extend %show-more;
|
||||
border-top: 1px solid transparent;
|
||||
margin-top: -5px;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#footer {
|
||||
.footer-categories {
|
||||
margin: 0 auto;
|
||||
|
||||
&-wrapper {
|
||||
margin-bottom: govuk-spacing(6);
|
||||
padding-bottom: govuk-spacing(9);
|
||||
border-bottom: 1px solid $grey-2;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 20px 0;
|
||||
|
||||
@include media(tablet) {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
$tail-angle: 20deg;
|
||||
|
||||
.sms-message-wrapper {
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 464px;
|
||||
box-sizing: border-box;
|
||||
padding: govuk-spacing(3);
|
||||
background: $panel-colour;
|
||||
border: 1px solid $panel-colour;
|
||||
border-radius: 5px;
|
||||
white-space: normal;
|
||||
margin: 0 0 govuk-spacing(6) 0;
|
||||
clear: both;
|
||||
word-wrap: break-word;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
right: -20px;
|
||||
border: 10px solid transparent;
|
||||
border-left-width: 13px;
|
||||
border-right-width: 13px;
|
||||
border-bottom-color: $panel-colour;
|
||||
border-left-color: $panel-colour;
|
||||
transform: rotate($tail-angle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sms-message-inbound {
|
||||
|
||||
.sms-message-wrapper {
|
||||
|
||||
&:after {
|
||||
border-left-color: transparent;
|
||||
border-bottom-color: $panel-colour;
|
||||
border-right-color: $panel-colour;
|
||||
right: auto;
|
||||
left: -20px;
|
||||
transform: rotate(-$tail-angle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sms-message-sender {
|
||||
@include copy-19;
|
||||
color: $secondary-text-colour;
|
||||
margin: 0 0 -10px 0;
|
||||
}
|
||||
|
||||
.sms-message-recipient {
|
||||
@include copy-19;
|
||||
color: $secondary-text-colour;
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
|
||||
.sms-message-status {
|
||||
@include core-16;
|
||||
color: $secondary-text-colour;
|
||||
margin: -20px govuk-spacing(3) 20px govuk-spacing(3);
|
||||
}
|
||||
|
||||
.sms-message-status-outbound {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sms-message-row {
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
padding-top: 120px;
|
||||
margin-top: -120px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sms-message-reply-link {
|
||||
text-align: right;
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
// CSS adapted from
|
||||
// https://github.com/alphagov/govuk_frontend_toolkit/blob/d9489a987086471fe30b4b925a81c12cd198c91d/docs/javascript.md#stick-at-top-when-scrolling
|
||||
|
||||
$sticky-padding: govuk-spacing(4);
|
||||
|
||||
.js-stick-at-top-when-scrolling,
|
||||
.js-stick-at-bottom-when-scrolling {
|
||||
|
||||
overflow: hidden;
|
||||
margin-left: -1 * govuk-spacing(3);
|
||||
padding: 10px 0 0 govuk-spacing(3);
|
||||
position: relative;
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
|
||||
legend {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.back-to-top-link {
|
||||
position: absolute;
|
||||
top: govuk-spacing(6);
|
||||
right: govuk-spacing(3);
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.js-stick-at-top-when-scrolling {
|
||||
|
||||
margin-top: -10px;
|
||||
margin-bottom: 5px;
|
||||
top: 5px;
|
||||
transition: top 0.1s ease-out, box-shadow 1s ease-in-out;
|
||||
|
||||
}
|
||||
|
||||
.js-stick-at-bottom-when-scrolling {
|
||||
|
||||
transition: bottom 0.1s ease-out, box-shadow 1s ease-in-out;
|
||||
padding: $sticky-padding 0 $sticky-padding govuk-spacing(3);
|
||||
margin-top: -$sticky-padding;
|
||||
|
||||
& + .js-stick-at-bottom-when-scrolling {
|
||||
margin-top: ($sticky-padding * 2) * -1;
|
||||
}
|
||||
|
||||
fieldset:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.notification-status {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.content-fixed,
|
||||
.content-fixed-onload {
|
||||
|
||||
position: fixed;
|
||||
background: $white;
|
||||
z-index: 100;
|
||||
padding-right: govuk-spacing(3);
|
||||
margin-top: 0;
|
||||
|
||||
.back-to-top-link {
|
||||
opacity: 1;
|
||||
transition: opacity 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.js-stick-at-top-when-scrolling.content-fixed,
|
||||
.js-stick-at-top-when-scrolling.content-fixed-onload {
|
||||
|
||||
top: 0;
|
||||
margin-top: 0;
|
||||
|
||||
}
|
||||
|
||||
.js-stick-at-top-when-scrolling.content-fixed__top {
|
||||
|
||||
border-bottom: 1px solid $border-colour;
|
||||
box-shadow: 0 2px 0 0 rgba($border-colour, 0.2);
|
||||
|
||||
}
|
||||
|
||||
.js-stick-at-top-when-scrolling.content-fixed {
|
||||
|
||||
transition: background 0.6s ease-in-out, margin-top 0.4s ease-out;
|
||||
|
||||
}
|
||||
|
||||
.js-stick-at-bottom-when-scrolling.content-fixed,
|
||||
.js-stick-at-bottom-when-scrolling.content-fixed-onload {
|
||||
|
||||
top: auto; // cancel `top: 0;` inherited from govuk-template
|
||||
bottom: 0;
|
||||
|
||||
}
|
||||
|
||||
.js-stick-at-bottom-when-scrolling.content-fixed__bottom {
|
||||
|
||||
border-top: 1px solid $border-colour;
|
||||
box-shadow: 0 -2px 0 0 rgba($border-colour, 0.2);
|
||||
|
||||
}
|
||||
|
||||
.js-stick-at-bottom-when-scrolling.content-fixed {
|
||||
|
||||
transition: background 0.6s ease-in-out;
|
||||
|
||||
}
|
||||
|
||||
.js-stick-at-bottom-when-scrolling-loaded.content-fixed-onload {
|
||||
|
||||
transition: none;
|
||||
|
||||
}
|
||||
|
||||
.shim {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.js-cancel {
|
||||
|
||||
display: inline-block;
|
||||
padding: 10px 10px 5px 10px;
|
||||
margin-top: -10px;
|
||||
margin-right: -10px;
|
||||
text-decoration: underline;
|
||||
color: $govuk-blue;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: $link-hover-colour;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
/* override default box shadow to stop it looking so large vertically */
|
||||
box-shadow: inset 0 -4px $govuk-focus-text-colour;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,452 +0,0 @@
|
||||
.table {
|
||||
margin-bottom: govuk-spacing(6);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-heading {
|
||||
text-align: left;
|
||||
margin: govuk-spacing(3) 0 govuk-spacing(3) 0;
|
||||
}
|
||||
|
||||
.dashboard-table {
|
||||
|
||||
.heading-medium {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.table-field-headings {
|
||||
th {
|
||||
font-size: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-field-headings-visible {
|
||||
th {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-field-heading-first {
|
||||
width: 52.5%;
|
||||
}
|
||||
|
||||
.table-row {
|
||||
th {
|
||||
display: table-cell;
|
||||
width: 52.5%;
|
||||
font-weight: normal;
|
||||
|
||||
.hint,
|
||||
p {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.big-number-label {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.template-statistics-table {
|
||||
|
||||
.table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.table-heading {
|
||||
@include core-19;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.table-field-heading-first {
|
||||
width: 52.5%;
|
||||
}
|
||||
|
||||
.table-row {
|
||||
th {
|
||||
display: table-cell;
|
||||
width: 52.5%;
|
||||
font-weight: normal;
|
||||
|
||||
.hint,
|
||||
p {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-template-name {
|
||||
|
||||
@include bold-24;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 10px 0 32px 0;
|
||||
margin: -10px 0 -32px 0;
|
||||
|
||||
&:focus {
|
||||
|
||||
color: $text-colour;
|
||||
|
||||
& + .template-statistics-table-hint {
|
||||
color: $text-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-hint {
|
||||
@include core-19;
|
||||
color: $secondary-text-colour;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.settings-table {
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
margin-bottom: govuk-spacing(1);
|
||||
}
|
||||
|
||||
th {
|
||||
|
||||
&:first-child {
|
||||
width: 35%; // 33.33% + fudge
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
width: 17.5% // 16.67% + fudge
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
td.table-field-left-aligned {
|
||||
|
||||
&:first-child {
|
||||
|
||||
div {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
div {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
ul li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
td.table-field-wrap-text {
|
||||
|
||||
div {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
ul li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.table-heading {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.usage-table {
|
||||
|
||||
.table-field-heading-first {
|
||||
width: 35%; // 33.33% + fudge
|
||||
}
|
||||
|
||||
.table-row {
|
||||
th {
|
||||
width: 17.5% // 16.67% + fudge
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%table-field,
|
||||
.table-field {
|
||||
|
||||
vertical-align: top;
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&-error {
|
||||
|
||||
border-left: 5px solid $error-colour;
|
||||
padding-left: 7px;
|
||||
display: block;
|
||||
|
||||
&-label {
|
||||
display: block;
|
||||
color: $error-colour;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-invisible-error {
|
||||
border-left: 5px solid transparent;
|
||||
padding-left: 7px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&-status {
|
||||
|
||||
&-default {
|
||||
color: $secondary-text-colour;
|
||||
}
|
||||
|
||||
&-error {
|
||||
|
||||
color: $error-colour;
|
||||
font-weight: bold;
|
||||
|
||||
.status-hint {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
color: $red;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-sent-international {
|
||||
|
||||
.status-hint {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-yes,
|
||||
&-no {
|
||||
display: block;
|
||||
text-indent: -999em;
|
||||
background-size: 19px 19px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
|
||||
&-yes {
|
||||
background-image: file-url('tick.png');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-noborder {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
&-index {
|
||||
|
||||
@include bold-16;
|
||||
width: 15px;
|
||||
|
||||
a {
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
|
||||
box-shadow: none;
|
||||
|
||||
&:before {
|
||||
background: $govuk-focus-colour;
|
||||
box-shadow: inset 0 -4px 0 0 $govuk-focus-text-colour;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.table-font-xsmall td.table-field-index { // overrides GOV.UK Elements
|
||||
@include bold-16;
|
||||
}
|
||||
|
||||
|
||||
.table-field-headings {
|
||||
|
||||
th {
|
||||
padding: 1px; /* needs some height for the grey border to show */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.table-field-headings-visible {
|
||||
|
||||
height: auto;
|
||||
|
||||
th {
|
||||
padding: .75em 1.25em .5625em 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.table-field-headings,
|
||||
.table-field-headings-visible {
|
||||
|
||||
th {
|
||||
@include bold-19;
|
||||
}
|
||||
|
||||
.dashboard-table &-first {
|
||||
width: 52.5%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.table-field-heading {
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&-right-aligned {
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.table-field-right-aligned {
|
||||
|
||||
@extend %table-field;
|
||||
text-align: right;
|
||||
|
||||
a {
|
||||
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
/* remove default focus styles in favour of those for :before pseudo class */
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: block;
|
||||
|
||||
position: absolute;
|
||||
|
||||
top: -1 * govuk-spacing(3);
|
||||
right: 0;
|
||||
bottom: -1 * govuk-spacing(3) + 4px;
|
||||
left: -1 * govuk-spacing(3);
|
||||
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:active:before,
|
||||
&:focus:before {
|
||||
box-shadow: inset 0px -4px $govuk-focus-text-colour, inset 0px 15px $govuk-focus-colour, inset 15px 0px $govuk-focus-colour, inset 0px -11px $govuk-focus-colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.table-row-group {
|
||||
border-top: 1px solid $border-colour;
|
||||
border-bottom: 1px solid $border-colour;
|
||||
}
|
||||
|
||||
.table-empty-message,
|
||||
td.table-empty-message {
|
||||
@include core-19;
|
||||
color: $secondary-text-colour;
|
||||
border-bottom: 1px solid $border-colour;
|
||||
padding: 20px 0 20px 0;
|
||||
}
|
||||
|
||||
.table-show-more-link {
|
||||
|
||||
@include core-16;
|
||||
color: $secondary-text-colour;
|
||||
margin-bottom: govuk-spacing(7);
|
||||
border-bottom: 1px solid $border-colour;
|
||||
padding: 35px 0 10px 0;
|
||||
text-align: center;
|
||||
|
||||
.table + & {
|
||||
margin-top: -1 * govuk-spacing(6);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
a.table-show-more-link {
|
||||
color: $link-colour;
|
||||
}
|
||||
|
||||
.table-no-data {
|
||||
@include core-16;
|
||||
color: $secondary-text-colour;
|
||||
margin-top: 10px;
|
||||
margin-bottom: govuk-spacing(7);
|
||||
border-top: 1px solid $border-colour;
|
||||
border-bottom: 1px solid $border-colour;
|
||||
padding: 0.75em 0 0.5625em 0;
|
||||
}
|
||||
|
||||
.wide-left-hand-column {
|
||||
display: block;
|
||||
max-width: 560px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.truncate-text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.spark-bar-bar {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
$indicator-colour: $black;
|
||||
|
||||
%task-list-indicator {
|
||||
@include bold-16;
|
||||
display: inline-block;
|
||||
padding: 3px 8px 1px 8px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: govuk-spacing(6) - 2px;
|
||||
margin-top: -15px;
|
||||
border: 2px solid $indicator-colour;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
min-width: 20%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.task-list {
|
||||
|
||||
border-top: 1px solid $border-colour;
|
||||
margin: govuk-spacing(6) 0;
|
||||
|
||||
&-item {
|
||||
|
||||
position: relative;
|
||||
|
||||
a {
|
||||
border-bottom: 1px solid $border-colour;
|
||||
display: block;
|
||||
padding: govuk-spacing(3) 0;
|
||||
padding-right: 20%;
|
||||
position: relative;
|
||||
|
||||
&:focus {
|
||||
box-shadow: inset 0 -4px $govuk-focus-text-colour;
|
||||
border-color: transparent;
|
||||
top: -1px;
|
||||
margin-bottom: -2px;
|
||||
padding-top: govuk-spacing(3) + 1px;
|
||||
padding-bottom: govuk-spacing(3) + 1px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-indicator-completed {
|
||||
@extend %task-list-indicator;
|
||||
background-color: $indicator-colour;
|
||||
color: $grey-4;
|
||||
// Just a pinch of letter spacing to make reversed-out text a bit
|
||||
// easier to read
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
&-indicator-not-completed {
|
||||
@extend %task-list-indicator;
|
||||
background-color: $white;
|
||||
color: $indicator-colour;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
.textbox-highlight {
|
||||
|
||||
$tag-background: rgba($light-blue, 0.6);
|
||||
|
||||
&-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-textbox {
|
||||
resize: none;
|
||||
z-index: 20;
|
||||
background: none;
|
||||
}
|
||||
|
||||
&-textbox,
|
||||
&-background,
|
||||
&-foreground,
|
||||
&-mask {
|
||||
font-size: 19px;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
overflow: hidden;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
&-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
color: transparent;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
border: 2px solid transparent;
|
||||
padding-bottom: govuk-spacing(3);
|
||||
z-index: 10;
|
||||
|
||||
// transparent borders become visible in high contrast modes so set to match background
|
||||
@media (-ms-high-contrast: active), (forced-colors: active) {
|
||||
border-color: Canvas;
|
||||
}
|
||||
|
||||
.placeholder,
|
||||
.placeholder-conditional {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.textbox-help-link {
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
|
||||
.textbox-right-aligned {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.extra-tracking .govuk-input {
|
||||
@include core-19($tabular-numbers: true);
|
||||
padding-left: 5px;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.textbox-colour-preview {
|
||||
@include media(desktop) {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
margin-left: 5px;
|
||||
border-radius: 50%;
|
||||
box-shadow: inset 0 0 0 1px rgba($black, 0.2);
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
transition: background 0.3s ease-out;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
%tick-cross {
|
||||
@include core-19;
|
||||
display: inline-block;
|
||||
background-size: 19px 19px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 6px;
|
||||
padding: 6px 0 5px 25px;
|
||||
}
|
||||
|
||||
.tick-cross {
|
||||
|
||||
&-tick {
|
||||
@extend %tick-cross;
|
||||
background-image: file-url('tick.png');
|
||||
}
|
||||
|
||||
&-cross {
|
||||
@extend %tick-cross;
|
||||
color: $secondary-text-colour;
|
||||
background-image: file-url('cross-grey.svg');
|
||||
}
|
||||
|
||||
&-list {
|
||||
|
||||
position: relative;
|
||||
|
||||
&-permissions {
|
||||
|
||||
margin-top: 5px;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-hint {
|
||||
color: #6F777B;
|
||||
padding-top: 5px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/**
|
||||
* Breadcrumbs Component
|
||||
*
|
||||
* Based on the existing breadcrumbs from GOV.UK Elements, re-implemented
|
||||
* using BEM, allowing for an `inverse` modifier (e.g. against a masthead), with
|
||||
* structured data markup, and allowing for the 'current page' to be included
|
||||
* as the last element in the breadcrumb.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* - Link the current page to the same place as your skip link (e.g. #main) –
|
||||
* this means that it is included by screen readers but is referred to as a
|
||||
* 'same page' link, and clicking it does not reload the page.
|
||||
* - You can test the structured data using Google's structured data testing
|
||||
* tool: https://search.google.com/structured-data/testing-tool
|
||||
*
|
||||
* Example Usage:
|
||||
*
|
||||
* <nav class="breadcrumbs" aria-label="Breadcrumbs">
|
||||
* <ol itemscope itemtype="http://schema.org/BreadcrumbList">
|
||||
* <li class="breadcrumbs__item" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
|
||||
* <a href="https://www.gov.uk/service-toolkit#components" itemprop="item"><span itemprop="name">Components</span></a>
|
||||
* </li>
|
||||
* <li class="breadcrumbs__item breadcrumbs__item--active" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
|
||||
* <a href="#main" itemprop="item"><span itemprop="name">Product Page</span></a>
|
||||
* </li>
|
||||
* </ol>
|
||||
* </nav>
|
||||
*/
|
||||
|
||||
.breadcrumbs {
|
||||
@include govuk-width-container;
|
||||
@include core-16($line-height: (25 / 16), $line-height-640: 1.75);
|
||||
|
||||
padding: govuk-spacing(2) 0;
|
||||
list-style: none;
|
||||
|
||||
@include media(tablet) {
|
||||
margin-bottom: govuk-spacing(6);
|
||||
}
|
||||
|
||||
ol {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: inline-block;
|
||||
|
||||
margin: 0;
|
||||
padding: 0 (govuk-spacing(6) / 4) 0 (11px + govuk-spacing(6) / 4);
|
||||
|
||||
background-image: file-url('separator.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 6px 11px;
|
||||
background-position: left center;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
@include device-pixel-ratio(2) {
|
||||
background-image: file-url('separator-2x.png');
|
||||
}
|
||||
|
||||
&,
|
||||
a:link,
|
||||
a:hover,
|
||||
a:active,
|
||||
a:visited {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
&--active {
|
||||
@include bold-16;
|
||||
|
||||
a:link,
|
||||
a:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
|
||||
&--inverse {
|
||||
border-bottom: 1px $white solid;
|
||||
border-bottom-color: rgba($white, 0.25);
|
||||
|
||||
.breadcrumbs__item {
|
||||
&--active,
|
||||
a:link,
|
||||
a:hover,
|
||||
a:active,
|
||||
a:visited {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
a:focus {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
Taken from the GOV.UK component at
|
||||
https://github.com/alphagov/static/blob/3d93a762b9d7af54615c77ae3e479131c03b8175/app/assets/stylesheets/govuk-component/_previous-and-next-navigation.scss
|
||||
and
|
||||
https://github.com/alphagov/static/blob/da8aeeaa749093eab30286d7fc9f965533b66f47/app/assets/stylesheets/styleguide/_conditionals2.scss
|
||||
*/
|
||||
|
||||
// Media query helpers. These make producing IE layouts
|
||||
// super easy.
|
||||
|
||||
// These are desktop and down media queries
|
||||
|
||||
// There is also a local version of this in Smartanswers.
|
||||
|
||||
$is-ie: false !default;
|
||||
|
||||
@mixin media-down($size: false, $max-width: false, $min-width: false) {
|
||||
@if $is-ie == false {
|
||||
@if $size == mobile {
|
||||
@media (max-width: 640px){
|
||||
@content;
|
||||
}
|
||||
} @else if $size == tablet {
|
||||
@media (max-width: 800px){
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.govuk-previous-and-next-navigation {
|
||||
display: block;
|
||||
margin-top: govuk-spacing(6);
|
||||
margin-bottom: govuk-spacing(6);
|
||||
margin-left: -1 * govuk-spacing(3);
|
||||
margin-right: -1 * govuk-spacing(3);
|
||||
@include govuk-clearfix;
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
@include core-16($line-height: (20 / 16));
|
||||
float: left;
|
||||
list-style: none;
|
||||
text-align: right;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 50%;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: govuk-spacing(3);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
background-color: $canvas-colour;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: $govuk-focus-colour;
|
||||
}
|
||||
|
||||
.pagination-part-title {
|
||||
@include core-27($line-height: (33.75 / 27));
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.previous-page {
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.next-page {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@include media-down(mobile) {
|
||||
.previous-page,
|
||||
.next-page {
|
||||
float: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.next-page a {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-icon {
|
||||
display: inline-block;
|
||||
margin-bottom: 1px;
|
||||
height: .482em;
|
||||
width: .63em;
|
||||
}
|
||||
|
||||
.pagination-label {
|
||||
display: inline-block;
|
||||
margin-top: 0.1em;
|
||||
text-decoration: underline;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Responsive Embed Component
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* - Ensure you use either responsive-embed--16by9 or responsive-embed--4by3
|
||||
* depending on the aspect ratio of your embed.
|
||||
* - Credit: Nicolas Gallagher and SUIT CSS.
|
||||
*
|
||||
* Example Usage:
|
||||
*
|
||||
* <div class="responsive-embed responsive-embed--4by3 responsive-embed--bordered">
|
||||
* <div class="responsive-embed__wrapper">
|
||||
* <iframe width="560" height="315" src="https://www.youtube.com/watch?v=dQw4w9WgXcQ" frameborder="0" allowfullscreen=""></iframe>
|
||||
* </div>
|
||||
* </div>
|
||||
*/
|
||||
|
||||
.responsive-embed {
|
||||
.responsive-embed__wrapper {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.responsive-embed__item,
|
||||
iframe,
|
||||
embed,
|
||||
object,
|
||||
video {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.responsive-embed--bordered {
|
||||
padding: 5px;
|
||||
outline: 1px solid $border-colour;
|
||||
}
|
||||
|
||||
// Modifier class for 16:9 aspect ratio
|
||||
.responsive-embed--16by9 {
|
||||
.responsive-embed__wrapper {
|
||||
padding-bottom: 56.25%;
|
||||
}
|
||||
}
|
||||
|
||||
// Modifier class for 4:3 aspect ratio
|
||||
.responsive-embed--4by3 {
|
||||
.responsive-embed__wrapper {
|
||||
padding-bottom: 75%;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
@import "settings/typography-font-families";
|
||||
|
||||
// needed for IE10 desktop snap mode: http://menacingcloud.com/?c=cssViewportOrMetaTag
|
||||
@-ms-viewport {
|
||||
width: device-width;
|
||||
}
|
||||
|
||||
// To be removed when all text uses the GOV.UK Frontend New Transport font styles.
|
||||
// At present, some text gets these styles due to being in a GOV.UK Frontend component
|
||||
// or a link.
|
||||
// This ensures all text will have these styles applied, until it can be moved to use
|
||||
// the GOV.UK Frontend font styles.
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.js-enabled .js-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// To be removed when all search inputs use the GOV.UK Frontend text input component:
|
||||
// https://design-system.service.gov.uk/components/text-input/
|
||||
/*
|
||||
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
|
||||
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
|
||||
* (include `-moz` to future-proof).
|
||||
*/
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box; /* 2 */
|
||||
box-sizing: content-box;
|
||||
}
|
||||
input[type="search"]::-webkit-search-cancel-button {
|
||||
-webkit-appearance: searchfield-cancel-button;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
// Each selector, and then the whole block when only one remains, to be removed when the
|
||||
// element comes from the corresponding GOV.UK Frontend component:
|
||||
// - https://design-system.service.gov.uk/components/text-input/
|
||||
// - https://design-system.service.gov.uk/components/textarea/
|
||||
// - https://design-system.service.gov.uk/components/select/
|
||||
// - https://design-system.service.gov.uk/components/button/
|
||||
|
||||
// Global styles for form controls
|
||||
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
outline: 3px solid $focus-colour;
|
||||
outline-offset: 0;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Version of node_modules/govuk-frontend/components/all.scss specific to Notify
|
||||
// Included to allow us to only include the components we need
|
||||
// All imports come from node_modules/govuk-frontend
|
||||
|
||||
// turn on compatibility mode for all legacy frameworks
|
||||
// https://github.com/alphagov/govuk-frontend/blob/master/docs/installation/compatibility.md#turn-on-compatibility-mode
|
||||
// to be removed when these frameworks are removed.
|
||||
$govuk-compatibility-govukfrontendtoolkit: true;
|
||||
$govuk-compatibility-govukelements: true;
|
||||
|
||||
// set asset URL root to match that of application
|
||||
$govuk-assets-path: "/static/";
|
||||
|
||||
@import "settings/all";
|
||||
// update to focus styles, remove when upgrading to GOVUK Frontend 3.x.x
|
||||
@import "./focus/settings";
|
||||
@import "tools/all";
|
||||
@import "helpers/all";
|
||||
// update to focus styles, remove when upgrading to GOVUK Frontend 3.x.x
|
||||
@import "./focus/helpers";
|
||||
|
||||
@import "core/all";
|
||||
// update to focus styles, remove when upgrading to GOVUK Frontend 3.x.x
|
||||
@import "./focus/core";
|
||||
@import "objects/all";
|
||||
|
||||
// section replacing @import "components/all", specifying which components to include
|
||||
@import "components/skip-link/_skip-link";
|
||||
@import "components/header/_header";
|
||||
@import "components/footer/_footer";
|
||||
@import "components/back-link/_back-link";
|
||||
@import "components/button/_button";
|
||||
@import "components/details/_details";
|
||||
@import "components/radios/_radios";
|
||||
@import "components/checkboxes/_checkboxes";
|
||||
@import "components/input/_input";
|
||||
@import "components/inset-text/_inset-text";
|
||||
@import "components/textarea/_textarea";
|
||||
|
||||
// update to focus styles, remove when upgrading to GOVUK Frontend 3.x.x
|
||||
@import "./focus/components";
|
||||
|
||||
@import "utilities/all";
|
||||
@import "overrides/all";
|
||||
|
||||
// Styles extending those from GOV.UK Frontend
|
||||
@import "./extensions";
|
||||
// update to focus styles, remove when upgrading to GOVUK Frontend 3.x.x
|
||||
@import "./focus/extensions";
|
||||
|
||||
// Styles for GOV.UK Frontend components specific to this application
|
||||
@import "./overrides";
|
||||
@@ -1,96 +0,0 @@
|
||||
// Gives access to the Sass variables used in the GOVUK Frontend typographic styles
|
||||
// See: https://frontend.design-system.service.gov.uk/sass-api-reference/#govuk-typography-scale
|
||||
@function get-govuk-typography-style($size, $breakpoint, $property) {
|
||||
$size-map: map-get($govuk-typography-scale, $size);
|
||||
$breakpoint-map: map-get($size-map, $breakpoint);
|
||||
|
||||
@if not map-has-key($breakpoint-map, $property) {
|
||||
@error "Unknown property #{$property} - expected a property from the typography scale for #{$size}.";
|
||||
}
|
||||
|
||||
@return map-get($breakpoint-map, $property);
|
||||
}
|
||||
|
||||
// Extends footer column styles to allow 4 columns
|
||||
@include mq ($from: desktop) {
|
||||
.govuk-footer__list--columns-4 {
|
||||
// TODO: Move support for legacy properties to something like Post-CSS's auto-prefixer
|
||||
-webkit-column-count: 4;
|
||||
-moz-column-count: 4;
|
||||
column-count: 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Extends govuk-link to create a class of link that causes a destructive action
|
||||
// Based on styles of link in:
|
||||
// https://github.com/alphagov/govuk-frontend/blob/v2.13.0/src/helpers/_links.scss
|
||||
//
|
||||
// Note: all destructive actions must have a confirmation step these links navigate to
|
||||
@mixin govuk-link-style-destructive-no-visited-state {
|
||||
&:link,
|
||||
&:visited {
|
||||
color: $govuk-error-colour;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: govuk-tint($govuk-error-colour, 25%);
|
||||
}
|
||||
|
||||
// When focussed, the text colour needs to be darker to ensure that colour
|
||||
// contrast is still acceptable against the focus colour
|
||||
// Activated links are usually focused so this applies to them as well
|
||||
&:active,
|
||||
&:focus {
|
||||
color: $govuk-focus-text-colour;
|
||||
}
|
||||
}
|
||||
|
||||
.govuk-link--destructive {
|
||||
@include govuk-link-style-destructive-no-visited-state;
|
||||
}
|
||||
$govuk-grid-widths: (
|
||||
one-eighth: 12.5%,
|
||||
one-sixth: 16.6666%,
|
||||
one-quarter: 25%,
|
||||
one-third: 33.3333%,
|
||||
one-half: 50%,
|
||||
five-eighths: 62.5%,
|
||||
two-thirds: 66.6666%,
|
||||
three-quarters: 75%,
|
||||
five-sixths: 83.3333%,
|
||||
seven-eighths: 87.5%,
|
||||
full: 100%
|
||||
);
|
||||
|
||||
.govuk-input--width-6 {
|
||||
max-width: 14ex;
|
||||
}
|
||||
|
||||
// extensions to govuk-input to allow numbering by label
|
||||
.govuk-input--numbered {
|
||||
padding-left: 1.84em;
|
||||
|
||||
@include media(desktop) {
|
||||
@include inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.govuk-input--numbered__label {
|
||||
float: left;
|
||||
width: 1.6em;
|
||||
margin: 8px -1.6em 0 0;
|
||||
position: relative;
|
||||
left: 10px;
|
||||
color: $govuk-secondary-text-colour;
|
||||
font-weight: bold;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.govuk-input--numbered__label--error {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
|
||||
@include govuk-media-query(desktop) {
|
||||
bottom: 7px;
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
// Sass bringing in the new focus style from GOVUK Frontend 3.x.x
|
||||
// TO DO: Delete this file when we upgrade to GOVUK Frontend 3.x.x
|
||||
//
|
||||
// See the following for details of the update:
|
||||
// - https://designnotes.blog.gov.uk/2019/07/29/weve-made-the-gov-uk-design-system-more-accessible/
|
||||
// - https://design-system.service.gov.uk/get-started/focus-states/
|
||||
// - https://github.com/alphagov/govuk-frontend/releases/tag/v3.0.0
|
||||
//
|
||||
// These styles were added in https://github.com/alphagov/govuk-frontend/pull/1309
|
||||
|
||||
// Updates to skip-link component
|
||||
.govuk-skip-link {
|
||||
@include govuk-typography-common;
|
||||
@include govuk-focusable-fill;
|
||||
}
|
||||
|
||||
// Updates to header component
|
||||
.govuk-header__link {
|
||||
@include govuk-focusable-text-link;
|
||||
}
|
||||
|
||||
.govuk-header__link--homepage {
|
||||
// Remove any borders that show when focused and hovered.
|
||||
&:focus {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates to footer component
|
||||
.govuk-footer__link {
|
||||
@include govuk-focusable-text-link;
|
||||
}
|
||||
|
||||
// Updates to back-link component
|
||||
.govuk-back-link {
|
||||
@include govuk-focusable-text-link;
|
||||
|
||||
// When the back link is focused, hide the bottom link border as the
|
||||
// focus styles has a bottom border.
|
||||
&:focus {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates to details component
|
||||
.govuk-details__summary {
|
||||
&:hover {
|
||||
color: $govuk-link-hover-colour;
|
||||
}
|
||||
|
||||
@include govuk-focusable-text-link;
|
||||
}
|
||||
|
||||
// Remove the underline when focussed to avoid duplicate borders
|
||||
.govuk-details__summary:focus .govuk-details__summary-text {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.govuk-details__summary:before {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
// Updates to buttons
|
||||
.govuk-button {
|
||||
&:focus {
|
||||
border-color: $govuk-focus-colour;
|
||||
// When colours are overridden, for example when users have a dark mode,
|
||||
// backgrounds and box-shadows disappear, so we need to ensure there's a
|
||||
// transparent outline which will be set to a visible colour.
|
||||
// Since Internet Explorer 8 does not support box-shadow, we want to force the user-agent outlines
|
||||
@include govuk-not-ie8 {
|
||||
outline: $govuk-focus-width solid transparent;
|
||||
outline-offset: 0;
|
||||
}
|
||||
// Since Internet Explorer does not support `:not()` we set a clearer focus style to match user-agent outlines.
|
||||
@include govuk-if-ie8 {
|
||||
color: $govuk-text-colour;
|
||||
background-color: $govuk-focus-colour;
|
||||
}
|
||||
box-shadow: inset 0 0 0 1px $govuk-focus-colour;
|
||||
}
|
||||
|
||||
&:focus:not(:active):not(:hover) {
|
||||
border-color: $govuk-focus-colour;
|
||||
color: $govuk-text-colour;
|
||||
background-color: $govuk-focus-colour;
|
||||
box-shadow: 0 2px 0 $govuk-focus-text-colour;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates to form inputs
|
||||
.govuk-input {
|
||||
&:focus {
|
||||
// Double the border by adding its width again. Use `box-shadow` for this // instead of changing `border-width` - this is for consistency with
|
||||
// components such as textarea where we avoid changing `border-width` as
|
||||
// it will change the element size. Also, `outline` cannot be utilised
|
||||
// here as it is already used for the yellow focus state.
|
||||
box-shadow: inset 0 0 0 $govuk-border-width-form-element;
|
||||
}
|
||||
}
|
||||
.govuk-input--error {
|
||||
&:focus {
|
||||
border-color: $govuk-input-border-colour;
|
||||
// Remove `box-shadow` inherited from `:focus` as `input--error`
|
||||
// already has the thicker border.
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates to form textareas (hacked to work with GOVUK Elements version)
|
||||
.form-control {
|
||||
@include govuk-focusable;
|
||||
|
||||
&:focus {
|
||||
// Double the border by adding its width again. Use `box-shadow` to do
|
||||
// this instead of changing `border-width` (which changes element size) and
|
||||
// since `outline` is already used for the yellow focus state.
|
||||
box-shadow: inset 0 0 0 $govuk-border-width-form-element;
|
||||
}
|
||||
}
|
||||
.form-control-error {
|
||||
&:focus {
|
||||
border-color: $govuk-input-border-colour;
|
||||
// Remove `box-shadow` inherited from `:focus` as `input--error`
|
||||
// already has the thicker border.
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates to form radios (hacked to work with GOVUK Elements version)
|
||||
|
||||
$govuk-radios-size: 40px;
|
||||
// When the default focus width is used on a curved edge it looks visually smaller.
|
||||
// So for the circular radios we bump the default to make it look visually consistent.
|
||||
$govuk-radios-focus-width: $govuk-focus-width + 1px;
|
||||
|
||||
// ( ) Radio ring
|
||||
.multiple-choice [type=radio] + label::before {
|
||||
content: "";
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: $govuk-radios-size;
|
||||
height: $govuk-radios-size;
|
||||
|
||||
border: $govuk-border-width-form-element solid currentColor;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
// • Radio button
|
||||
//
|
||||
// We create the 'button' entirely out of 'border' so that they remain
|
||||
// 'filled' even when colours are overridden in the browser.
|
||||
.multiple-choice [type=radio] + label::after {
|
||||
content: "";
|
||||
|
||||
position: absolute;
|
||||
top: govuk-spacing(2);
|
||||
left: govuk-spacing(2);
|
||||
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
border: govuk-spacing(2) solid currentColor;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
// Focused state (includes targeting for GOVUK radios, to apply version 3.x.x styles)
|
||||
.multiple-choice [type=radio]:focus + label::before,
|
||||
.govuk-radios__input:focus + .govuk-radios__label::before {
|
||||
border-width: 4px;
|
||||
// Since box-shadows are removed when users customise their colours we set a
|
||||
// transparent outline that is shown instead.
|
||||
// https://accessibility.blog.gov.uk/2017/03/27/how-users-change-colours-on-websites/
|
||||
outline: $govuk-focus-width solid transparent;
|
||||
outline-offset: $govuk-focus-width;
|
||||
box-shadow: 0 0 0 $govuk-radios-focus-width $govuk-focus-colour;
|
||||
}
|
||||
|
||||
// Selected state
|
||||
.multiple-choice [type=radio]:checked + label::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// Updates to form checkboxes
|
||||
.govuk-checkboxes__input:focus + .govuk-checkboxes__label::before {
|
||||
border-width: 4px;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Sass bringing in the new focus style from GOVUK Frontend 3.x.x
|
||||
// TO DO: Delete this file when we upgrade to GOVUK Frontend 3.x.x
|
||||
//
|
||||
// See the following for details of the update:
|
||||
// - https://designnotes.blog.gov.uk/2019/07/29/weve-made-the-gov-uk-design-system-more-accessible/
|
||||
// - https://design-system.service.gov.uk/get-started/focus-states/
|
||||
// - https://github.com/alphagov/govuk-frontend/releases/tag/v3.0.0
|
||||
//
|
||||
// These styles were added in https://github.com/alphagov/govuk-frontend/pull/1309
|
||||
|
||||
// Hack to fix not being able to override the govuk-link-common mixin
|
||||
.govuk-link {
|
||||
@include govuk-focusable-text-link;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Sass bringing in the new focus style from GOVUK Frontend 3.x.x
|
||||
// TO DO: Delete this file when we upgrade to GOVUK Frontend 3.x.x
|
||||
//
|
||||
// See the following for details of the update:
|
||||
// - https://designnotes.blog.gov.uk/2019/07/29/weve-made-the-gov-uk-design-system-more-accessible/
|
||||
// - https://design-system.service.gov.uk/get-started/focus-states/
|
||||
// - https://github.com/alphagov/govuk-frontend/releases/tag/v3.0.0
|
||||
//
|
||||
// These styles were added in https://github.com/alphagov/govuk-frontend/pull/1309
|
||||
// (for the error summary component, which we don't use yet)
|
||||
|
||||
.govuk-link--destructive:focus {
|
||||
@include govuk-focusable-text-link;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Sass bringing in the new focus style from GOVUK Frontend 3.x.x
|
||||
// TO DO: Delete this file when we upgrade to GOVUK Frontend 3.x.x
|
||||
//
|
||||
// See the following for details of the update:
|
||||
// - https://designnotes.blog.gov.uk/2019/07/29/weve-made-the-gov-uk-design-system-more-accessible/
|
||||
// - https://design-system.service.gov.uk/get-started/focus-states/
|
||||
// - https://github.com/alphagov/govuk-frontend/releases/tag/v3.0.0
|
||||
//
|
||||
// These styles were added in https://github.com/alphagov/govuk-frontend/pull/1309
|
||||
// and edited in https://github.com/alphagov/govuk-frontend/pull/1455
|
||||
//
|
||||
/// Focusable with box-shadow
|
||||
///
|
||||
/// Removes the visible outline and replace with box-shadow and background colour.
|
||||
/// Used for interactive text-based elements.
|
||||
|
||||
@mixin govuk-focusable-text-link {
|
||||
&:focus {
|
||||
// When colours are overridden, for example when users have a dark mode,
|
||||
// backgrounds and box-shadows disappear, so we need to ensure there's a
|
||||
// transparent outline which will be set to a visible colour.
|
||||
|
||||
outline: $govuk-focus-width solid transparent;
|
||||
color: $govuk-focus-text-colour;
|
||||
background-color: $govuk-focus-colour;
|
||||
box-shadow: 0 -2px $govuk-focus-colour, 0 4px $govuk-focus-text-colour;
|
||||
// When link is focussed, hide the default underline since the
|
||||
// box shadow adds the "underline"
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||