-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add CommandPanel component and CommandDictionary * pull dictionary logic out from SelectedCommand into SequenceEditor for reuse * add command dictionary argument components
- Loading branch information
Showing
30 changed files
with
1,884 additions
and
362 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<svelte:options immutable={true} /> | ||
|
||
<script lang="ts"> | ||
import type { FswCommandArgument } from '@nasa-jpl/aerie-ampcs'; | ||
import { | ||
isFswCommandArgumentBoolean, | ||
isFswCommandArgumentEnum, | ||
isFswCommandArgumentRepeat, | ||
isNumberArg, | ||
isStringArg, | ||
} from '../../../utilities/codemirror/codemirror-utils'; | ||
import CommandBooleanArgDef from './CommandBooleanArgDef.svelte'; | ||
import CommandEnumArgDef from './CommandEnumArgDef.svelte'; | ||
import CommandNumberArgDef from './CommandNumberArgDef.svelte'; | ||
import CommandRepeatArgDef from './CommandRepeatArgDef.svelte'; | ||
import CommandStringArgDef from './CommandStringArgDef.svelte'; | ||
export let commandArgumentDefinition: FswCommandArgument; | ||
</script> | ||
|
||
<div> | ||
{#if isFswCommandArgumentBoolean(commandArgumentDefinition)} | ||
<CommandBooleanArgDef argDef={commandArgumentDefinition} /> | ||
{:else if isStringArg(commandArgumentDefinition)} | ||
<CommandStringArgDef argDef={commandArgumentDefinition} /> | ||
{:else if isNumberArg(commandArgumentDefinition)} | ||
<CommandNumberArgDef argDef={commandArgumentDefinition} /> | ||
{:else if isFswCommandArgumentEnum(commandArgumentDefinition)} | ||
<CommandEnumArgDef argDef={commandArgumentDefinition} /> | ||
{:else if isFswCommandArgumentRepeat(commandArgumentDefinition)} | ||
<CommandRepeatArgDef argDef={commandArgumentDefinition} /> | ||
{/if} | ||
</div> |
184 changes: 184 additions & 0 deletions
184
src/components/sequencing/CommandPanel/CommandArg.svelte.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import type { | ||
FswCommandArgument, | ||
FswCommandArgumentBoolean, | ||
FswCommandArgumentEnum, | ||
FswCommandArgumentFloat, | ||
FswCommandArgumentInteger, | ||
FswCommandArgumentNumeric, | ||
FswCommandArgumentRepeat, | ||
FswCommandArgumentUnsigned, | ||
FswCommandArgumentVarString, | ||
} from '@nasa-jpl/aerie-ampcs'; | ||
import { cleanup, render } from '@testing-library/svelte'; | ||
import { keyBy } from 'lodash-es'; | ||
import { afterEach, describe, expect, it } from 'vitest'; | ||
import CommandArg from './CommandArg.svelte'; | ||
|
||
describe('CommandArg component', () => { | ||
afterEach(() => { | ||
cleanup(); | ||
}); | ||
|
||
it('Should render a boolean command argument', () => { | ||
const booleanArgument: FswCommandArgumentBoolean = { | ||
arg_type: 'boolean', | ||
bit_length: 1, | ||
default_value: 'Foo true', | ||
description: 'test boolean arg', | ||
format: { | ||
false_str: 'Foo false', | ||
true_str: 'Foo true', | ||
}, | ||
name: 'Foo Boolean', | ||
}; | ||
const { getByText } = render(CommandArg, { commandArgumentDefinition: booleanArgument }); | ||
expect(getByText(/Foo false/)).toBeDefined(); | ||
expect(getByText(/Foo true/)).toBeDefined(); | ||
}); | ||
|
||
it('Should render an enum command argument', () => { | ||
const enumArgument: FswCommandArgumentEnum = { | ||
arg_type: 'enum', | ||
bit_length: 3, | ||
default_value: 'Foo true', | ||
description: 'test enum arg', | ||
enum_name: 'Foo enum name', | ||
name: 'Foo Enum', | ||
range: ['bar', 'baz', 'buzz'], | ||
}; | ||
const { getByDisplayValue } = render(CommandArg, { commandArgumentDefinition: enumArgument }); | ||
expect(getByDisplayValue(/bar/)).toBeDefined(); | ||
expect(getByDisplayValue(/baz/)).toBeDefined(); | ||
expect(getByDisplayValue(/buzz/)).toBeDefined(); | ||
}); | ||
|
||
describe('number command arguments', () => { | ||
it('Should render a float command argument', () => { | ||
const floatArgument: FswCommandArgumentFloat = { | ||
arg_type: 'float', | ||
bit_length: 3, | ||
default_value: 10, | ||
description: 'test float arg', | ||
name: 'Foo Float', | ||
range: { | ||
max: 20, | ||
min: 0, | ||
}, | ||
units: 'foo', | ||
}; | ||
const { getByText } = render(CommandArg, { commandArgumentDefinition: floatArgument }); | ||
expect(getByText(/0/)).toBeDefined(); | ||
expect(getByText(/20/)).toBeDefined(); | ||
}); | ||
|
||
it('Should render an integer command argument', () => { | ||
const integerArgument: FswCommandArgumentInteger = { | ||
arg_type: 'integer', | ||
bit_length: 3, | ||
default_value: 10, | ||
description: 'test integer arg', | ||
name: 'Foo Integer', | ||
range: { | ||
max: 20, | ||
min: 0, | ||
}, | ||
units: 'foo', | ||
}; | ||
const { getByText } = render(CommandArg, { commandArgumentDefinition: integerArgument }); | ||
expect(getByText(/0/)).toBeDefined(); | ||
expect(getByText(/20/)).toBeDefined(); | ||
}); | ||
|
||
it('Should render an unsigned command argument', () => { | ||
const unsignedArgument: FswCommandArgumentUnsigned = { | ||
arg_type: 'unsigned', | ||
bit_length: 3, | ||
default_value: 10, | ||
description: 'test unsigned arg', | ||
name: 'Foo Unsigned', | ||
range: { | ||
max: 20, | ||
min: 0, | ||
}, | ||
units: 'foo', | ||
}; | ||
const { getByText } = render(CommandArg, { commandArgumentDefinition: unsignedArgument }); | ||
expect(getByText(/0/)).toBeDefined(); | ||
expect(getByText(/20/)).toBeDefined(); | ||
}); | ||
|
||
it('Should render an unsigned command argument', () => { | ||
const numericArgument: FswCommandArgumentNumeric = { | ||
arg_type: 'numeric', | ||
bit_length: 3, | ||
default_value: 10, | ||
description: 'test numeric arg', | ||
name: 'Foo Numeric', | ||
range: { | ||
max: 20, | ||
min: 0, | ||
}, | ||
type: 'float', | ||
units: 'foo', | ||
}; | ||
const { getByText } = render(CommandArg, { commandArgumentDefinition: numericArgument }); | ||
expect(getByText(/0/)).toBeDefined(); | ||
expect(getByText(/20/)).toBeDefined(); | ||
}); | ||
}); | ||
|
||
it('Should render a string command argument', () => { | ||
const stringArgument: FswCommandArgumentVarString = { | ||
arg_type: 'var_string', | ||
default_value: 'Foo string', | ||
description: 'test string arg', | ||
max_bit_length: 8, | ||
name: 'Foo String', | ||
prefix_bit_length: 2, | ||
valid_regex: '', | ||
}; | ||
const { getByText } = render(CommandArg, { commandArgumentDefinition: stringArgument }); | ||
expect(getByText(/test string arg/)).toBeDefined(); | ||
}); | ||
|
||
it('Should render a repeating command argument', () => { | ||
const commandArguments: FswCommandArgument[] = [ | ||
{ | ||
arg_type: 'boolean', | ||
bit_length: 1, | ||
default_value: 'Foo true', | ||
description: 'test boolean arg', | ||
format: { | ||
false_str: 'Foo false', | ||
true_str: 'Foo true', | ||
}, | ||
name: 'Boolean', | ||
}, | ||
{ | ||
arg_type: 'var_string', | ||
default_value: 'Foo string', | ||
description: 'test string arg', | ||
max_bit_length: 8, | ||
name: 'String', | ||
prefix_bit_length: 2, | ||
valid_regex: '', | ||
}, | ||
]; | ||
const repeatArgument: FswCommandArgumentRepeat = { | ||
arg_type: 'repeat', | ||
description: 'test repeating arg', | ||
name: 'Foo Repeat', | ||
prefix_bit_length: 2, | ||
repeat: { | ||
argumentMap: keyBy(commandArguments, 'name'), | ||
arguments: commandArguments, | ||
max: 4, | ||
min: 1, | ||
}, | ||
}; | ||
const { getByText } = render(CommandArg, { commandArgumentDefinition: repeatArgument }); | ||
expect(getByText(/test repeating arg/)).toBeDefined(); | ||
expect(getByText(/test boolean arg/)).toBeDefined(); | ||
expect(getByText(/test string arg/)).toBeDefined(); | ||
}); | ||
}); |
91 changes: 91 additions & 0 deletions
91
src/components/sequencing/CommandPanel/CommandArgUnit.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
<svelte:options immutable={true} /> | ||
|
||
<script lang="ts"> | ||
import InclusiveRangeIcon from '../../../assets/inclusive-range.svg?component'; | ||
import RulerIcon from '../../../assets/ruler.svg?component'; | ||
import { tooltip } from '../../../utilities/tooltip'; | ||
export let type: 'string' | 'boolean' | 'number' | 'enum' | 'repeat'; | ||
export let typeDisplay: string | undefined = undefined; | ||
export let unit: string | undefined = undefined; | ||
export let unitShortName: string | undefined = undefined; | ||
export let range: { max: number | string; min: number | string } | undefined = undefined; | ||
function isValidUnit(unitDisplay: string | undefined, unitShortNameDisplay: string | undefined) { | ||
const unitToCheck = unitDisplay || unitShortNameDisplay; | ||
if (unitToCheck) { | ||
switch (unitToCheck.toLowerCase()) { | ||
case 'n/a': | ||
case 'none': | ||
return false; | ||
default: | ||
return true; | ||
} | ||
} | ||
} | ||
</script> | ||
|
||
<div class="unit-container"> | ||
{#if typeDisplay} | ||
<div> | ||
<div class="type faded"><RulerIcon /></div> | ||
{typeDisplay} | ||
</div> | ||
{/if} | ||
{#if range} | ||
{#if type === 'boolean'} | ||
<div> | ||
{range.min}<span class="faded">|</span>{range.max} | ||
</div> | ||
{:else} | ||
<div> | ||
<div class="type faded"><InclusiveRangeIcon /></div> | ||
{range.min}<span class="faded">-</span>{range.max} | ||
</div> | ||
{/if} | ||
{/if} | ||
{#if isValidUnit(unit, unitShortName)} | ||
<div class="unit" use:tooltip={{ content: unit }}> | ||
<div class="dot faded"></div> | ||
{unitShortName || unit} | ||
</div> | ||
{/if} | ||
</div> | ||
|
||
<style> | ||
.unit-container { | ||
color: #000; | ||
column-gap: 4px; | ||
display: flex; | ||
flex-flow: row; | ||
} | ||
.unit-container > div { | ||
align-items: center; | ||
background: var(--st-gray-10); | ||
border: 1px solid var(--st-gray-15); | ||
border-radius: 2px; | ||
display: flex; | ||
gap: 4px; | ||
padding: 2px 4px; | ||
} | ||
.type { | ||
align-items: center; | ||
display: flex; | ||
} | ||
.faded { | ||
opacity: 0.6; | ||
} | ||
.unit .dot { | ||
border-color: var(--st-gray-100); | ||
border-radius: 50%; | ||
border-style: solid; | ||
border-width: 1px; | ||
height: 6px; | ||
width: 6px; | ||
} | ||
</style> |
58 changes: 58 additions & 0 deletions
58
src/components/sequencing/CommandPanel/CommandArgUnit.svelte.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { cleanup, render } from '@testing-library/svelte'; | ||
import { afterEach, describe, expect, it } from 'vitest'; | ||
import CommandArgUnit from './CommandArgUnit.svelte'; | ||
|
||
describe('CommandArgUnit component', () => { | ||
afterEach(() => { | ||
cleanup(); | ||
}); | ||
|
||
it('Should render units for a boolean command argument', () => { | ||
const { getByText } = render(CommandArgUnit, { | ||
range: { max: 'Foo true', min: 'Foo false' }, | ||
type: 'boolean', | ||
typeDisplay: 'Foo boolean', | ||
}); | ||
expect(getByText(/Foo false/)).toBeDefined(); | ||
expect(getByText(/Foo true/)).toBeDefined(); | ||
}); | ||
|
||
it('Should render a range for a command argument', () => { | ||
const { getByText } = render(CommandArgUnit, { | ||
range: { max: '24', min: '2' }, | ||
type: 'number', | ||
typeDisplay: 'Foo number', | ||
}); | ||
expect(getByText(/2/)).toBeDefined(); | ||
expect(getByText(/-/)).toBeDefined(); | ||
expect(getByText(/24/)).toBeDefined(); | ||
}); | ||
|
||
it('Should render units for a string command argument', () => { | ||
const { getByText } = render(CommandArgUnit, { | ||
type: 'string', | ||
typeDisplay: 'Foo string', | ||
}); | ||
expect(getByText(/Foo string/)).toBeDefined(); | ||
}); | ||
|
||
it('Should render short version of unit', () => { | ||
const { getByText, queryByText } = render(CommandArgUnit, { | ||
type: 'string', | ||
typeDisplay: 'Foo string', | ||
unit: 'bar baz', | ||
unitShortName: 'foo', | ||
}); | ||
expect(getByText('foo')).toBeDefined(); | ||
expect(queryByText('bar baz')).toBeNull(); | ||
}); | ||
|
||
it('Should render unit if no short version is provided', () => { | ||
const { getByText } = render(CommandArgUnit, { | ||
type: 'string', | ||
typeDisplay: 'Foo string', | ||
unit: 'bar baz', | ||
}); | ||
expect(getByText('bar baz')).toBeDefined(); | ||
}); | ||
}); |
Oops, something went wrong.