diff --git a/ui/admin/app/components/ordered-series-diagram/group/index.hbs b/ui/admin/app/components/ordered-series-diagram/group/index.hbs
new file mode 100644
index 000000000..8dc61ad6a
--- /dev/null
+++ b/ui/admin/app/components/ordered-series-diagram/group/index.hbs
@@ -0,0 +1,25 @@
+
+
+
+
+ {{@title}}
+
+
+ {{yield}}
+
+
+
+
+ {{!
+ Separator icon is not configurable, but potentially could be.
+ }}
+
+
+
+
\ No newline at end of file
diff --git a/ui/admin/app/components/ordered-series-diagram/index.hbs b/ui/admin/app/components/ordered-series-diagram/index.hbs
new file mode 100644
index 000000000..b0d67eb67
--- /dev/null
+++ b/ui/admin/app/components/ordered-series-diagram/index.hbs
@@ -0,0 +1,8 @@
+
+ {{yield
+ (hash
+ Item=(component 'ordered-series-diagram/item')
+ Group=(component 'ordered-series-diagram/group')
+ )
+ }}
+
\ No newline at end of file
diff --git a/ui/admin/app/components/ordered-series-diagram/item/index.hbs b/ui/admin/app/components/ordered-series-diagram/item/index.hbs
new file mode 100644
index 000000000..551b7c6a8
--- /dev/null
+++ b/ui/admin/app/components/ordered-series-diagram/item/index.hbs
@@ -0,0 +1,17 @@
+
+
+
+
+
+ {{yield}}
+
+
+
+
+ {{!
+ Separator icon is not configurable, but potentially could be.
+ }}
+
+
+
+
\ No newline at end of file
diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss
index a62ab83f6..36df9ae8b 100644
--- a/ui/admin/app/styles/app.scss
+++ b/ui/admin/app/styles/app.scss
@@ -598,6 +598,71 @@
}
}
+.ordered-series-diagram {
+ --group-title-line-height: 2.25rem;
+
+ display: flex;
+ margin-bottom: 1rem;
+
+ &-item {
+ display: flex;
+ margin-top: var(--group-title-line-height);
+
+ &-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ }
+
+ &-title {
+ font-weight: var(--token-typography-font-weight-semibold);
+ margin-top: 0.5rem;
+ width: 4rem;
+ }
+
+ &:last-child {
+ > .ordered-series-diagram-separator-icon {
+ display: none;
+ }
+ }
+ }
+
+ &-group {
+ display: flex;
+
+ &-content {
+ border-radius: 0.5rem;
+ padding: 0 0.75rem 0.75rem;
+ }
+
+ &-items {
+ display: flex;
+
+ & .ordered-series-diagram-item {
+ margin-top: 0;
+ }
+ }
+
+ &-title {
+ font-size: var(--token-typography-display-100-font-size);
+ line-height: var(--group-title-line-height);
+ text-align: center;
+ }
+
+ &:last-child {
+ > .ordered-series-diagram-separator-icon {
+ display: none;
+ }
+ }
+ }
+
+ &-separator-icon {
+ color: var(--token-color-foreground-faint);
+ margin: 0.75rem 0.5rem;
+ }
+}
+
.target-workers {
margin-top: sizing.rems(xxl);
diff --git a/ui/admin/tests/integration/components/ordered-series-diagram/group/index-test.js b/ui/admin/tests/integration/components/ordered-series-diagram/group/index-test.js
new file mode 100644
index 000000000..2642f75f3
--- /dev/null
+++ b/ui/admin/tests/integration/components/ordered-series-diagram/group/index-test.js
@@ -0,0 +1,44 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'admin/tests/helpers';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module(
+ 'Integration | Component | ordered-series-diagram/group/index',
+ function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders group icons with highlight background', async function (assert) {
+ assert.expect(7);
+
+ await render(hbs`
+
+
+ Client
+
+
+ Egress Worker
+
+
+ `);
+
+ assert
+ .dom('.ordered-series-diagram-group-content')
+ .hasClass('hds-surface-highlight');
+ assert
+ .dom('.ordered-series-diagram-group-title')
+ .hasText('Private Network');
+ assert.dom('[data-test-icon="user"]').isVisible();
+ assert.dom('[data-test-icon="arrow-right"]').isVisible();
+ assert
+ .dom('.ordered-series-diagram-item .ordered-series-diagram-item-title')
+ .hasText('Client');
+ assert.dom('[data-test-icon="settings"]').isVisible();
+ assert
+ .dom(
+ '.ordered-series-diagram-item:last-child .ordered-series-diagram-item-title'
+ )
+ .hasText('Egress Worker');
+ });
+ }
+);
diff --git a/ui/admin/tests/integration/components/ordered-series-diagram/index-test.js b/ui/admin/tests/integration/components/ordered-series-diagram/index-test.js
new file mode 100644
index 000000000..8cd0a3077
--- /dev/null
+++ b/ui/admin/tests/integration/components/ordered-series-diagram/index-test.js
@@ -0,0 +1,31 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'admin/tests/helpers';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module(
+ 'Integration | Component | ordered-series-diagram/index',
+ function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders two icon series', async function (assert) {
+ assert.expect(5);
+ await render(hbs`
+
+ Client
+ Egress Worker
+
+ `);
+
+ assert.dom('[data-test-icon="user"]').isVisible();
+ assert.dom('.ordered-series-diagram-item-title').hasText('Client');
+ assert.dom('[data-test-icon="arrow-right"]').isVisible();
+ assert.dom('[data-test-icon="settings"]').isVisible();
+ assert
+ .dom(
+ '.ordered-series-diagram-item:last-child .ordered-series-diagram-item-title'
+ )
+ .hasText('Egress Worker');
+ });
+ }
+);
diff --git a/ui/admin/tests/integration/components/ordered-series-diagram/item/index-test.js b/ui/admin/tests/integration/components/ordered-series-diagram/item/index-test.js
new file mode 100644
index 000000000..458f1954e
--- /dev/null
+++ b/ui/admin/tests/integration/components/ordered-series-diagram/item/index-test.js
@@ -0,0 +1,24 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'admin/tests/helpers';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module(
+ 'Integration | Component | ordered-series-diagram/item/index',
+ function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders with icon and text', async function (assert) {
+ assert.expect(2);
+
+ await render(hbs`
+
+ Client
+
+ `);
+
+ assert.dom('[data-test-icon="user"]').isVisible();
+ assert.dom('.ordered-series-diagram-item-title').hasText('Client');
+ });
+ }
+);