@nordicsemiconductor/pc-nrfconnect-shared 220.0.0 → 223.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/Changelog.md +27 -0
  2. package/README.md +30 -21
  3. package/config/tailwind.config.js +5 -1
  4. package/ipc/apps.ts +3 -12
  5. package/ipc/sources.ts +0 -5
  6. package/package.json +1 -1
  7. package/release_notes.md +3 -6
  8. package/scripts/check-app-properties.ts +1 -1
  9. package/scripts/check-for-typescript.ts +1 -1
  10. package/scripts/create-source.ts +1 -1
  11. package/scripts/esbuild.ts +1 -1
  12. package/scripts/installHusky.ts +1 -1
  13. package/scripts/is-releasable.ts +2 -2
  14. package/scripts/latest-changelog-entry.test.ts +4 -4
  15. package/scripts/latest-changelog-entry.ts +1 -1
  16. package/scripts/nordic-publish.js +1 -1
  17. package/scripts/nordic-publish.ts +1 -1
  18. package/scripts/nrfconnect-license.ts +1 -1
  19. package/scripts/postinstall.ts +1 -1
  20. package/scripts/prepare-shared-release.ts +1 -1
  21. package/src/Device/DeviceSelector/DeviceSelector.test.tsx +11 -11
  22. package/src/Dialog/Dialog.test.tsx +9 -10
  23. package/src/Dropdown/Dropdown.test.tsx +3 -3
  24. package/src/Dropdown/Dropdown.tsx +15 -4
  25. package/src/Dropdown/DropdownHelpers.test.ts +20 -21
  26. package/src/ErrorBoundary/ErrorBoundary.test.tsx +4 -4
  27. package/src/ErrorDialog/ErrorDialog.test.tsx +3 -3
  28. package/src/ErrorDialog/errorDialogSlice.test.ts +5 -5
  29. package/src/FactoryReset/FactoryResetButton.test.tsx +23 -17
  30. package/src/Group/Group.tsx +1 -1
  31. package/src/InlineInput/InlineInput.tsx +3 -0
  32. package/src/InlineInput/NumberInlineInput.tsx +23 -0
  33. package/src/Slider/Factor.test.ts +4 -4
  34. package/src/index.ts +0 -2
  35. package/src/logging/appTransport.test.ts +6 -6
  36. package/src/logging/logBuffer.test.ts +4 -4
  37. package/typings/generated/ipc/apps.d.ts +5 -11
  38. package/typings/generated/ipc/apps.d.ts.map +1 -1
  39. package/typings/generated/ipc/sources.d.ts +0 -7
  40. package/typings/generated/ipc/sources.d.ts.map +1 -1
  41. package/typings/generated/main/index.d.ts +1 -9
  42. package/typings/generated/main/index.d.ts.map +1 -1
  43. package/typings/generated/scripts/check-app-properties.d.ts +1 -1
  44. package/typings/generated/scripts/check-for-typescript.d.ts +1 -1
  45. package/typings/generated/scripts/create-source.d.ts +1 -1
  46. package/typings/generated/scripts/esbuild.d.ts +1 -1
  47. package/typings/generated/scripts/installHusky.d.ts +1 -1
  48. package/typings/generated/scripts/is-releasable.d.ts +1 -1
  49. package/typings/generated/scripts/latest-changelog-entry.d.ts +1 -1
  50. package/typings/generated/scripts/nordic-publish.d.ts +1 -1
  51. package/typings/generated/scripts/nrfconnect-license.d.ts +1 -1
  52. package/typings/generated/scripts/postinstall.d.ts +1 -1
  53. package/typings/generated/scripts/prepare-shared-release.d.ts +1 -1
  54. package/typings/generated/src/Dropdown/Dropdown.d.ts +2 -1
  55. package/typings/generated/src/Dropdown/Dropdown.d.ts.map +1 -1
  56. package/typings/generated/src/InlineInput/InlineInput.d.ts +1 -0
  57. package/typings/generated/src/InlineInput/InlineInput.d.ts.map +1 -1
  58. package/typings/generated/src/InlineInput/NumberInlineInput.d.ts.map +1 -1
  59. package/typings/generated/src/index.d.ts +1 -1
  60. package/typings/generated/src/index.d.ts.map +1 -1
package/Changelog.md CHANGED
@@ -7,6 +7,33 @@ This project does _not_ adhere to
7
7
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html) but contrary to it
8
8
  every new version is a new major version.
9
9
 
10
+ ## 223.0.0 - 2025-07-28
11
+
12
+ ### Changed
13
+
14
+ - Return value of `getDownloadableApps` has less fields now and some more
15
+ exports were removed, because they were only needed in the launcher and
16
+ having them in shared made changes harder in the launcher.
17
+
18
+ ## 222.0.0 - 2025-07-23
19
+
20
+ ### Added
21
+
22
+ - Automatic publishing of shared.
23
+
24
+ ## 221.0.0 - 2025-07-21
25
+
26
+ ### Added
27
+
28
+ - Custom tailwind font-size `text-2xs` (0.625rem/10px).
29
+ - Narrower Dropdown variant.
30
+ - Automatic character limit derivation for number input components based on
31
+ their range constraints.
32
+
33
+ ### Fixed
34
+
35
+ - Running build scripts failed on Windows.
36
+
10
37
  ## 220.0.0 - 2025-07-16
11
38
 
12
39
  ### Added
package/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # Shared commodities for developing nRF Connect for Desktop
2
2
 
3
- [![Build Status](https://dev.azure.com/NordicSemiconductor/Wayland/_apis/build/status/pc-nrfconnect-shared?branchName=master)](https://dev.azure.com/NordicSemiconductor/Wayland/_build/latest?definitionId=31&branchName=master)
4
-
5
3
  This project provides shared commodities for developing nRF Connect for Desktop
6
4
  apps and their launcher:
7
5
 
@@ -10,30 +8,41 @@ apps and their launcher:
10
8
  - Configurations
11
9
  - Test facilities
12
10
 
11
+ ## Developing a new feature or fixing an error
12
+
13
+ Whenever something is changed in pc-nrfconnect-shared, an entry should be added
14
+ to `Changelog.md`.
15
+
16
+ If there is no latest entry there yet, and you do not intend to release the
17
+ change as a new version right ahead, add a new section with the heading
18
+ `## Unreleased` at the top.
19
+
13
20
  ## Releasing
14
21
 
15
- This project uses GitHub Actions for automated releases. To release a new
16
- version:
22
+ To release, two files must be up-to-date:
23
+
24
+ - `package.json` contain the correct version number (one more than the last
25
+ release).
26
+ - `Changelog.md` must contain an entry, with that version number and today's
27
+ date.
17
28
 
18
- ### Prepare the release
29
+ By running `npm run prepare-shared-release` you update the version in
30
+ `package.json` and in `Changelog.md` a potential `## Unreleased` heading is
31
+ updated to the right version and today‘s date.
19
32
 
20
- - Update the version in `package.json`.
21
- - Add a changelog entry in `Changelog.md` with the format:
22
- `## X.0.0 - YYYY-MM-DD`.
23
- - Commit and push your changes
33
+ When those conditions are met, a new release of shared will automatically be
34
+ created when the according PR is merged into main.
24
35
 
25
- ### Create the release
36
+ ## Unpublishing a version
26
37
 
27
- - Go to the the
28
- [Release shared](https://github.com/NordicSemiconductor/pc-nrfconnect-shared/actions/workflows/release-shared.yml)
29
- action.
30
- - Click "Run workflow".
31
- - Optionally select a ref to release, but usually this should be `main` and
32
- can be left empty.
38
+ If you need to unpublish a specific version from npm (e.g., due to a critical
39
+ bug), you can use the "Unpublish npm version" GitHub Action:
33
40
 
34
- The workflow will:
41
+ 1. Go to the
42
+ [Unpublish npm version GitHub Action](https://github.com/NordicSemiconductor/pc-nrfconnect-shared/actions/workflows/unpublish-npm-version.yml)
43
+ and run the workflow.
44
+ 1. Enter the version to unpublish (e.g., `221.0.0`)
35
45
 
36
- - Check if a release is possible.
37
- - Build and test the package.
38
- - Create a GitHub release.
39
- - Publish to npm.
46
+ **Warning:** Unpublishing a version from npm is irreversible and should only be
47
+ done in exceptional circumstances (e.g., security vulnerabilities, critical
48
+ bugs).
@@ -16,7 +16,11 @@ module.exports = {
16
16
  ],
17
17
  theme: {
18
18
  colors,
19
- extend: {},
19
+ extend: {
20
+ fontSize: {
21
+ '2xs': '0.625rem', // 10px
22
+ },
23
+ },
20
24
  },
21
25
  corePlugins: {
22
26
  preflight: false,
package/ipc/apps.ts CHANGED
@@ -11,7 +11,7 @@ import type {
11
11
  NrfutilModuleVersion,
12
12
  UrlString,
13
13
  } from './MetaFiles';
14
- import { LOCAL, Source, SourceName } from './sources';
14
+ import { LOCAL, SourceName } from './sources';
15
15
 
16
16
  export interface AppSpec {
17
17
  name: string;
@@ -75,18 +75,11 @@ export type LaunchableApp = LocalApp | InstalledDownloadableApp | WithdrawnApp;
75
75
 
76
76
  export type App = LocalApp | DownloadableApp;
77
77
 
78
- export interface AppWithError extends AppSpec {
79
- reason: unknown;
80
- path: string;
81
- }
82
-
83
- const channel = {
78
+ export const channel = {
84
79
  getDownloadableApps: 'apps:get-downloadable-apps',
85
80
  installDownloadableApp: 'apps:install-downloadable-app',
86
81
  };
87
82
 
88
- export type SourceWithError = { source: Source; reason?: string };
89
-
90
83
  export const isDownloadable = (app?: App): app is DownloadableApp =>
91
84
  app != null && app?.source !== LOCAL;
92
85
 
@@ -115,10 +108,8 @@ export const isUpdatable = (app?: App): app is InstalledDownloadableApp =>
115
108
  latestVersionHasDifferentChecksum(app));
116
109
 
117
110
  // getDownloadableApps
118
- type GetDownloadableAppsResult = {
111
+ export type GetDownloadableAppsResult = {
119
112
  apps: DownloadableApp[];
120
- appsWithErrors: AppWithError[];
121
- sourcesWithErrors: SourceWithError[];
122
113
  };
123
114
 
124
115
  type GetDownloadableApps = () => GetDownloadableAppsResult;
package/ipc/sources.ts CHANGED
@@ -4,16 +4,11 @@
4
4
  * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
5
  */
6
6
 
7
- import { UrlString } from './MetaFiles';
8
-
9
7
  enum StandardSourceNames {
10
8
  OFFICIAL = 'official',
11
9
  LOCAL = 'local',
12
10
  }
13
11
 
14
12
  export const { LOCAL, OFFICIAL } = StandardSourceNames;
15
- export const allStandardSourceNames: SourceName[] = [OFFICIAL, LOCAL];
16
13
 
17
14
  export type SourceName = string;
18
- export type SourceUrl = UrlString;
19
- export type Source = { name: SourceName; url: SourceUrl };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nordicsemiconductor/pc-nrfconnect-shared",
3
- "version": "220.0.0",
3
+ "version": "223.0.0",
4
4
  "description": "Shared commodities for developing pc-nrfconnect-* packages",
5
5
  "repository": {
6
6
  "type": "git",
package/release_notes.md CHANGED
@@ -1,8 +1,5 @@
1
- ### Added
2
-
3
- - Option to persist the Group collapse state.
4
-
5
1
  ### Changed
6
2
 
7
- - Use `tsx` instead of `ts-node`.
8
- - Switched releasing shared from Azure to GitHub Actions.
3
+ - Return value of `getDownloadableApps` has less fields now and some more
4
+ exports were removed, because they were only needed in the launcher and
5
+ having them in shared made changes harder in the launcher.
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2022 Nordic Semiconductor ASA
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2015 Nordic Semiconductor ASA
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2025 Nordic Semiconductor ASA
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
  /*
3
3
  * Copyright (c) 2015 Nordic Semiconductor ASA
4
4
  *
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2022 Nordic Semiconductor ASA
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2025 Nordic Semiconductor ASA
@@ -18,7 +18,7 @@ import getReleaseNumbers from './get-release-numbers';
18
18
  import { getLatestEntry } from './latest-changelog-entry';
19
19
 
20
20
  const fail = (message: string) => {
21
- console.log(`::error::${message}`);
21
+ console.log(message);
22
22
  process.exit(1);
23
23
  };
24
24
 
@@ -7,7 +7,7 @@
7
7
  import { getLatestEntry } from './latest-changelog-entry';
8
8
 
9
9
  describe('getLatestEntry', () => {
10
- it('should extract the latest changelog entry correctly', () => {
10
+ it('extracts the latest changelog entry', () => {
11
11
  const changelog = `# Changelog
12
12
 
13
13
  All notable changes to this project will be documented in this file.
@@ -29,7 +29,7 @@ All notable changes to this project will be documented in this file.
29
29
  expect(result.content).toBe('### Changed\n\n- Something');
30
30
  });
31
31
 
32
- it('should handle changelog with only one entry', () => {
32
+ it('handles changelog with only one entry', () => {
33
33
  const changelog = `# Changelog
34
34
 
35
35
  All notable changes to this project will be documented in this file.
@@ -46,7 +46,7 @@ All notable changes to this project will be documented in this file.
46
46
  expect(result.content).toBe('### Added\n\n- Something else');
47
47
  });
48
48
 
49
- it('should handle changelog with empty content', () => {
49
+ it('handles changelog with empty content', () => {
50
50
  const changelog = `# Changelog
51
51
 
52
52
  All notable changes to this project will be documented in this file.
@@ -65,7 +65,7 @@ All notable changes to this project will be documented in this file.
65
65
  expect(result.content).toBe('');
66
66
  });
67
67
 
68
- it('should handle changelog with nothing before the first entry', () => {
68
+ it('handles changelog with nothing before the first entry', () => {
69
69
  const changelog = `## 33.0.0 - 2022-02-01
70
70
 
71
71
  ### Added
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2025 Nordic Semiconductor ASA
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
  "use strict";var Cd=Object.create;var Us=Object.defineProperty;var gd=Object.getOwnPropertyDescriptor;var Dd=Object.getOwnPropertyNames;var _d=Object.getPrototypeOf,Bd=Object.prototype.hasOwnProperty;var A=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var yd=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Dd(e))!Bd.call(t,i)&&i!==r&&Us(t,i,{get:()=>e[i],enumerable:!(n=gd(e,i))||n.enumerable});return t};var Dt=(t,e,r)=>(r=t!=null?Cd(_d(t)):{},yd(e||!t||!t.__esModule?Us(r,"default",{value:t,enumerable:!0}):r,t));var jr=A(pi=>{var Cn=class extends Error{constructor(e,r,n){super(n),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name,this.code=r,this.exitCode=e,this.nestedError=void 0}},hi=class extends Cn{constructor(e){super(1,"commander.invalidArgument",e),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name}};pi.CommanderError=Cn;pi.InvalidArgumentError=hi});var gn=A(Fi=>{var{InvalidArgumentError:vd}=jr(),Ai=class{constructor(e,r){switch(this.description=r||"",this.variadic=!1,this.parseArg=void 0,this.defaultValue=void 0,this.defaultValueDescription=void 0,this.argChoices=void 0,e[0]){case"<":this.required=!0,this._name=e.slice(1,-1);break;case"[":this.required=!1,this._name=e.slice(1,-1);break;default:this.required=!0,this._name=e;break}this._name.length>3&&this._name.slice(-3)==="..."&&(this.variadic=!0,this._name=this._name.slice(0,-3))}name(){return this._name}_concatValue(e,r){return r===this.defaultValue||!Array.isArray(r)?[e]:r.concat(e)}default(e,r){return this.defaultValue=e,this.defaultValueDescription=r,this}argParser(e){return this.parseArg=e,this}choices(e){return this.argChoices=e.slice(),this.parseArg=(r,n)=>{if(!this.argChoices.includes(r))throw new vd(`Allowed choices are ${this.argChoices.join(", ")}.`);return this.variadic?this._concatValue(r,n):r},this}argRequired(){return this.required=!0,this}argOptional(){return this.required=!1,this}};function bd(t){let e=t.name()+(t.variadic===!0?"...":"");return t.required?"<"+e+">":"["+e+"]"}Fi.Argument=Ai;Fi.humanReadableArgName=bd});var mi=A(Hs=>{var{humanReadableArgName:xd}=gn(),Ei=class{constructor(){this.helpWidth=void 0,this.sortSubcommands=!1,this.sortOptions=!1,this.showGlobalOptions=!1}visibleCommands(e){let r=e.commands.filter(n=>!n._hidden);if(e._hasImplicitHelpCommand()){let[,n,i]=e._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/),s=e.createCommand(n).helpOption(!1);s.description(e._helpCommandDescription),i&&s.arguments(i),r.push(s)}return this.sortSubcommands&&r.sort((n,i)=>n.name().localeCompare(i.name())),r}compareOptions(e,r){let n=i=>i.short?i.short.replace(/^-/,""):i.long.replace(/^--/,"");return n(e).localeCompare(n(r))}visibleOptions(e){let r=e.options.filter(s=>!s.hidden),n=e._hasHelpOption&&e._helpShortFlag&&!e._findOption(e._helpShortFlag),i=e._hasHelpOption&&!e._findOption(e._helpLongFlag);if(n||i){let s;n?i?s=e.createOption(e._helpFlags,e._helpDescription):s=e.createOption(e._helpShortFlag,e._helpDescription):s=e.createOption(e._helpLongFlag,e._helpDescription),r.push(s)}return this.sortOptions&&r.sort(this.compareOptions),r}visibleGlobalOptions(e){if(!this.showGlobalOptions)return[];let r=[];for(let n=e.parent;n;n=n.parent){let i=n.options.filter(s=>!s.hidden);r.push(...i)}return this.sortOptions&&r.sort(this.compareOptions),r}visibleArguments(e){return e._argsDescription&&e._args.forEach(r=>{r.description=r.description||e._argsDescription[r.name()]||""}),e._args.find(r=>r.description)?e._args:[]}subcommandTerm(e){let r=e._args.map(n=>xd(n)).join(" ");return e._name+(e._aliases[0]?"|"+e._aliases[0]:"")+(e.options.length?" [options]":"")+(r?" "+r:"")}optionTerm(e){return e.flags}argumentTerm(e){return e.name()}longestSubcommandTermLength(e,r){return r.visibleCommands(e).reduce((n,i)=>Math.max(n,r.subcommandTerm(i).length),0)}longestOptionTermLength(e,r){return r.visibleOptions(e).reduce((n,i)=>Math.max(n,r.optionTerm(i).length),0)}longestGlobalOptionTermLength(e,r){return r.visibleGlobalOptions(e).reduce((n,i)=>Math.max(n,r.optionTerm(i).length),0)}longestArgumentTermLength(e,r){return r.visibleArguments(e).reduce((n,i)=>Math.max(n,r.argumentTerm(i).length),0)}commandUsage(e){let r=e._name;e._aliases[0]&&(r=r+"|"+e._aliases[0]);let n="";for(let i=e.parent;i;i=i.parent)n=i.name()+" "+n;return n+r+" "+e.usage()}commandDescription(e){return e.description()}subcommandDescription(e){return e.summary()||e.description()}optionDescription(e){let r=[];return e.argChoices&&r.push(`choices: ${e.argChoices.map(n=>JSON.stringify(n)).join(", ")}`),e.defaultValue!==void 0&&(e.required||e.optional||e.isBoolean()&&typeof e.defaultValue=="boolean")&&r.push(`default: ${e.defaultValueDescription||JSON.stringify(e.defaultValue)}`),e.presetArg!==void 0&&e.optional&&r.push(`preset: ${JSON.stringify(e.presetArg)}`),e.envVar!==void 0&&r.push(`env: ${e.envVar}`),r.length>0?`${e.description} (${r.join(", ")})`:e.description}argumentDescription(e){let r=[];if(e.argChoices&&r.push(`choices: ${e.argChoices.map(n=>JSON.stringify(n)).join(", ")}`),e.defaultValue!==void 0&&r.push(`default: ${e.defaultValueDescription||JSON.stringify(e.defaultValue)}`),r.length>0){let n=`(${r.join(", ")})`;return e.description?`${e.description} ${n}`:n}return e.description}formatHelp(e,r){let n=r.padWidth(e,r),i=r.helpWidth||80,s=2,a=2;function o(x,C){if(C){let T=`${x.padEnd(n+a)}${C}`;return r.wrap(T,i-s,n+a)}return x}function u(x){return x.join(`
3
3
  `).replace(/^/gm," ".repeat(s))}let c=[`Usage: ${r.commandUsage(e)}`,""],l=r.commandDescription(e);l.length>0&&(c=c.concat([r.wrap(l,i,0),""]));let f=r.visibleArguments(e).map(x=>o(r.argumentTerm(x),r.argumentDescription(x)));f.length>0&&(c=c.concat(["Arguments:",u(f),""]));let m=r.visibleOptions(e).map(x=>o(r.optionTerm(x),r.optionDescription(x)));if(m.length>0&&(c=c.concat(["Options:",u(m),""])),this.showGlobalOptions){let x=r.visibleGlobalOptions(e).map(C=>o(r.optionTerm(C),r.optionDescription(C)));x.length>0&&(c=c.concat(["Global Options:",u(x),""]))}let F=r.visibleCommands(e).map(x=>o(r.subcommandTerm(x),r.subcommandDescription(x)));return F.length>0&&(c=c.concat(["Commands:",u(F),""])),c.join(`
4
4
  `)}padWidth(e,r){return Math.max(r.longestOptionTermLength(e,r),r.longestGlobalOptionTermLength(e,r),r.longestSubcommandTermLength(e,r),r.longestArgumentTermLength(e,r))}wrap(e,r,n,i=40){let s=" \\f\\t\\v\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF",a=new RegExp(`[\\n][${s}]+`);if(e.match(a))return e;let o=r-n;if(o<i)return e;let u=e.slice(0,n),c=e.slice(n).replace(`\r
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2015 Nordic Semiconductor ASA
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2021 Nordic Semiconductor ASA
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2015 Nordic Semiconductor ASA
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S npx tsx
1
+ #!/usr/bin/env tsx
2
2
 
3
3
  /*
4
4
  * Copyright (c) 2023 Nordic Semiconductor ASA
@@ -105,7 +105,7 @@ const validFirmware = {
105
105
  };
106
106
 
107
107
  describe('DeviceSelector', () => {
108
- it('should have no device selected by default', () => {
108
+ it('has no device selected by default', () => {
109
109
  render(
110
110
  <DeviceSelector
111
111
  deviceListing={{
@@ -119,7 +119,7 @@ describe('DeviceSelector', () => {
119
119
  expect(screen.getByText('Select device')).toBeInTheDocument();
120
120
  });
121
121
 
122
- it('should show no connected devices', () => {
122
+ it('shows an information when no devices are connected', () => {
123
123
  render(
124
124
  <DeviceSelector
125
125
  deviceListing={{
@@ -134,7 +134,7 @@ describe('DeviceSelector', () => {
134
134
  expect(screen.getByText('Nordic development kit')).toBeInTheDocument();
135
135
  });
136
136
 
137
- it('should list connected devices', () => {
137
+ it('shows a list of connected devices', () => {
138
138
  render(
139
139
  <DeviceSelector
140
140
  deviceListing={{
@@ -149,7 +149,7 @@ describe('DeviceSelector', () => {
149
149
  expect(screen.getByText(DEVICE_SERIAL_NUMBER)).toBeInTheDocument();
150
150
  });
151
151
 
152
- it('should unlist disconnected devices', () => {
152
+ it('does not show disconnected devices', () => {
153
153
  render(
154
154
  <DeviceSelector
155
155
  deviceListing={{
@@ -164,7 +164,7 @@ describe('DeviceSelector', () => {
164
164
  expect(screen.queryByText(DEVICE_SERIAL_NUMBER)).toBeNull();
165
165
  });
166
166
 
167
- it('should show more device info when selecting the expand button', () => {
167
+ it('shows more device info when selecting the expand button', () => {
168
168
  render(
169
169
  <DeviceSelector
170
170
  deviceListing={{
@@ -181,7 +181,7 @@ describe('DeviceSelector', () => {
181
181
  expect(screen.getAllByText(/COM/)).toHaveLength(2);
182
182
  });
183
183
 
184
- it('can select connected devices', async () => {
184
+ it('allows selecting a device', async () => {
185
185
  render(
186
186
  <DeviceSelector
187
187
  deviceListing={{
@@ -203,7 +203,7 @@ describe('DeviceSelector', () => {
203
203
  expect(screen.getAllByText(DEVICE_SERIAL_NUMBER)).toHaveLength(2);
204
204
  });
205
205
 
206
- it('can deselect selected devices', async () => {
206
+ it('allows disconnecting a device', async () => {
207
207
  render(
208
208
  <DeviceSelector
209
209
  deviceListing={{
@@ -223,7 +223,7 @@ describe('DeviceSelector', () => {
223
223
  expect(screen.getByText('Select device')).toBeInTheDocument();
224
224
  });
225
225
 
226
- it('should allow device selection when custom devices are enabled and no valid firmware is defined', async () => {
226
+ it('allows selecting a device when custom devices are enabled and no valid firmware is defined', async () => {
227
227
  render(
228
228
  <DeviceSelector
229
229
  deviceListing={{
@@ -259,7 +259,7 @@ describe('DeviceSelector', () => {
259
259
  expect(screen.getAllByText(DEVICE_SERIAL_NUMBER)).toHaveLength(2);
260
260
  });
261
261
 
262
- it('should deselect device when custom devices are disabled and no valid firmware is defined', async () => {
262
+ it('allows deselecting a device when custom devices are disabled and no valid firmware is defined', async () => {
263
263
  render(
264
264
  <DeviceSelector
265
265
  deviceListing={{
@@ -295,7 +295,7 @@ describe('DeviceSelector', () => {
295
295
  await screen.findByText('Select device');
296
296
  });
297
297
 
298
- it('should show firmware prompt when a valid firmware is defined', async () => {
298
+ it('shows firmware prompt when a valid firmware is defined', async () => {
299
299
  render(
300
300
  <DeviceSelector
301
301
  deviceListing={{
@@ -317,7 +317,7 @@ describe('DeviceSelector', () => {
317
317
  );
318
318
  });
319
319
 
320
- it('should select device when cancelling firmware prompt', async () => {
320
+ it('selects a device when cancelling firmware prompt', async () => {
321
321
  render(
322
322
  <DeviceSelector
323
323
  deviceListing={{
@@ -28,18 +28,17 @@ describe('Dialog', () => {
28
28
  </Dialog>
29
29
  );
30
30
 
31
- test('is rendered when visible', () => {
31
+ it('is rendered when visible', () => {
32
32
  render(dialog());
33
33
  expect(screen.getByRole('dialog')).toBeInTheDocument();
34
- test;
35
34
  });
36
35
 
37
- test('is not rendered when not visible', () => {
36
+ it('is not rendered when not visible', () => {
38
37
  render(dialog(false));
39
38
  expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
40
39
  });
41
40
 
42
- test('shows the expected content', () => {
41
+ it('shows the expected content', () => {
43
42
  render(dialog());
44
43
 
45
44
  expect(screen.getByText('Test Title')).toBeInTheDocument();
@@ -55,7 +54,7 @@ describe('InfoDialog creator', () => {
55
54
  </InfoDialog>
56
55
  );
57
56
 
58
- test('shows the expected content', () => {
57
+ it('shows the expected content', () => {
59
58
  render(dialog());
60
59
 
61
60
  expect(screen.getByText('Info')).toBeInTheDocument();
@@ -63,7 +62,7 @@ describe('InfoDialog creator', () => {
63
62
  expect(screen.getByText('Close')).toBeInTheDocument();
64
63
  });
65
64
 
66
- test('invokes the expected action', () => {
65
+ it('invokes the expected action', () => {
67
66
  render(dialog());
68
67
 
69
68
  const closeButton = screen.getByText('Close');
@@ -80,7 +79,7 @@ describe('ErrorDialog creator', () => {
80
79
  </ErrorDialog>
81
80
  );
82
81
 
83
- test('shows the expected content', () => {
82
+ it('shows the expected content', () => {
84
83
  render(dialog());
85
84
 
86
85
  expect(screen.getByText('Error')).toBeInTheDocument();
@@ -88,7 +87,7 @@ describe('ErrorDialog creator', () => {
88
87
  expect(screen.getByText('Close')).toBeInTheDocument();
89
88
  });
90
89
 
91
- test('invokes the expected action', () => {
90
+ it('invokes the expected action', () => {
92
91
  render(dialog());
93
92
 
94
93
  const closeButton = screen.getByText('Close');
@@ -111,7 +110,7 @@ describe('ConfirmationDialog creator', () => {
111
110
  </ConfirmationDialog>
112
111
  );
113
112
 
114
- test('shows the expected content', () => {
113
+ it('shows the expected content', () => {
115
114
  render(dialog());
116
115
 
117
116
  expect(screen.getByText('Confirm')).toBeInTheDocument();
@@ -121,7 +120,7 @@ describe('ConfirmationDialog creator', () => {
121
120
  expect(screen.getByText('Cancel')).toBeInTheDocument();
122
121
  });
123
122
 
124
- test('invokes the expected action', () => {
123
+ it('invokes the expected action', () => {
125
124
  render(dialog());
126
125
 
127
126
  fireEvent.click(screen.getByText('Optional'));
@@ -22,7 +22,7 @@ const items = [
22
22
  ];
23
23
 
24
24
  describe('Dropdown', () => {
25
- it('renders a list of items', () => {
25
+ it('shows a list of items', () => {
26
26
  render(
27
27
  <Dropdown
28
28
  items={items}
@@ -34,7 +34,7 @@ describe('Dropdown', () => {
34
34
  expect(screen.getByText('Bar')).toBeInTheDocument();
35
35
  });
36
36
 
37
- it('calls onSelect when item is clicked', () => {
37
+ it('calls onSelect', () => {
38
38
  const onSelect = jest.fn();
39
39
  const item = items[1];
40
40
  render(
@@ -49,7 +49,7 @@ describe('Dropdown', () => {
49
49
  expect(onSelect).toHaveBeenCalledWith(item);
50
50
  });
51
51
 
52
- it('correct item is selected', () => {
52
+ it('selects the correct item', () => {
53
53
  render(
54
54
  <Dropdown
55
55
  items={items}
@@ -28,6 +28,7 @@ export type DropdownProps<T> = {
28
28
  selectedItem: DropdownItem<T>;
29
29
  numItemsBeforeScroll?: number;
30
30
  className?: string;
31
+ size?: 'sm' | 'md';
31
32
  };
32
33
 
33
34
  export default <T,>({
@@ -42,6 +43,7 @@ export default <T,>({
42
43
  selectedItem,
43
44
  numItemsBeforeScroll = 0,
44
45
  className = '',
46
+ size = 'md',
45
47
  }: DropdownProps<T>) => {
46
48
  const [isActive, setIsActive] = useState(false);
47
49
 
@@ -74,7 +76,12 @@ export default <T,>({
74
76
  minWidth ? '' : 'tw-w-full',
75
77
  transparentButtonBg
76
78
  ? 'tw-bg-transparent'
77
- : 'tw-h-8 tw-bg-gray-700 tw-px-2 tw-text-white'
79
+ : classNames(
80
+ 'tw-bg-gray-700 tw-text-white',
81
+ size === 'sm'
82
+ ? 'tw-h-6 tw-pl-2 tw-pr-1 tw-text-2xs'
83
+ : 'tw-h-8 tw-px-2'
84
+ )
78
85
  )}
79
86
  onClick={() => setIsActive(!isActive)}
80
87
  disabled={disabled}
@@ -85,8 +92,9 @@ export default <T,>({
85
92
  : selectedItem.label}
86
93
  </span>
87
94
  <span
88
- className={`mdi mdi-chevron-down tw-text-lg/none ${classNames(
89
- isActive && 'tw-rotate-180'
95
+ className={`mdi mdi-chevron-down ${classNames(
96
+ isActive && 'tw-rotate-180',
97
+ size === 'sm' ? 'tw-text-base' : 'tw-text-lg'
90
98
  )}`}
91
99
  />
92
100
  </button>
@@ -116,7 +124,10 @@ export default <T,>({
116
124
  {items.map(item => (
117
125
  <button
118
126
  type="button"
119
- className="tw-bg-transparent tw-clear-both tw-block tw-h-6 tw-w-full tw-whitespace-nowrap tw-border-0 tw-px-2 tw-py-1 tw-text-left tw-font-normal tw-text-white hover:tw-bg-gray-600 focus:tw-bg-gray-600"
127
+ className={classNames(
128
+ 'tw-bg-transparent tw-clear-both tw-block tw-h-6 tw-w-full tw-whitespace-nowrap tw-border-0 tw-px-2 tw-py-1 tw-text-left tw-font-normal tw-text-white hover:tw-bg-gray-600 focus:tw-bg-gray-600',
129
+ size === 'sm' && 'tw-text-2xs'
130
+ )}
120
131
  key={JSON.stringify(item.value)}
121
132
  onClick={() => onClickItem(item)}
122
133
  >
@@ -10,40 +10,39 @@ import {
10
10
  getSelectedDropdownItem,
11
11
  } from './DropdownHelpers';
12
12
 
13
+ const itemList = [
14
+ { label: 'foo label', value: 'foo' },
15
+ { label: 'bar label', value: 'bar' },
16
+ ];
17
+
13
18
  describe('getSelectedDropdownItem', () => {
19
+ it('returns the item with the correct value', () => {
20
+ expect(getSelectedDropdownItem(itemList, 'bar')).toBe(itemList[1]);
21
+ });
22
+
14
23
  it('returns the first item if value is undefined', () => {
15
- const itemList = [{ label: 'foo', value: 'foo' }];
16
24
  expect(getSelectedDropdownItem(itemList, undefined)).toBe(itemList[0]);
17
25
  });
18
26
 
19
27
  it('returns the first item if value is not found', () => {
20
- const itemList = [{ label: 'foo', value: 'foo' }];
21
- expect(getSelectedDropdownItem(itemList, 'bar')).toBe(itemList[0]);
22
- });
23
-
24
- it('returns the item with the correct value', () => {
25
- const itemList = [
26
- { label: 'foo', value: 'foo' },
27
- { label: 'bar', value: 'bar' },
28
- ];
29
- expect(getSelectedDropdownItem(itemList, 'bar')).toBe(itemList[1]);
30
- });
31
-
32
- it('returns the item with the correct value when value is a boolean', () => {
33
- const itemList = [
34
- { label: 'on', value: 'on' },
35
- { label: 'off', value: 'off' },
36
- ];
37
- expect(getSelectedDropdownItem(itemList, true)).toBe(itemList[0]);
28
+ expect(getSelectedDropdownItem(itemList, 'unknown')).toBe(itemList[0]);
38
29
  });
39
30
 
40
31
  it('returns the notFound item if value is not found', () => {
41
- const itemList = [{ label: 'foo', value: 'foo' }];
42
32
  const notFound = { label: 'not found', value: 'not found' };
43
- expect(getSelectedDropdownItem(itemList, 'bar', notFound)).toBe(
33
+ expect(getSelectedDropdownItem(itemList, 'unknown', notFound)).toBe(
44
34
  notFound
45
35
  );
46
36
  });
37
+
38
+ it('handles booleans as the strings `on` and `off`', () => {
39
+ const booleanList = [
40
+ { label: 'on label', value: 'on' },
41
+ { label: 'off label', value: 'off' },
42
+ ];
43
+ expect(getSelectedDropdownItem(booleanList, true).value).toBe('on');
44
+ expect(getSelectedDropdownItem(booleanList, false).value).toBe('off');
45
+ });
47
46
  });
48
47
 
49
48
  describe('convertToDropDownItems', () => {