Merge pull request 'Add operator info to place details' (#111) from feature/operator into master
Reviewed-on: #111
This commit was merged in pull request #111.
This commit is contained in:
@@ -316,6 +316,70 @@ export default class PlaceDetails extends Component {
|
||||
return this.formatMultiLine(val);
|
||||
}
|
||||
|
||||
get operatorNames() {
|
||||
const raw = this.tags.operator;
|
||||
if (!raw) return null;
|
||||
const values = raw
|
||||
.split(';')
|
||||
.map((value) => value.trim())
|
||||
.filter(
|
||||
(value) =>
|
||||
value && value.toLowerCase() !== 'yes' && value.toLowerCase() !== 'no'
|
||||
);
|
||||
if (values.length === 0) return null;
|
||||
return this.formatMultiLine(values.join(';'));
|
||||
}
|
||||
|
||||
get operatorWebsite() {
|
||||
const val = this.tags['operator:website'];
|
||||
return val ? val.trim() : null;
|
||||
}
|
||||
|
||||
get operatorWebsiteLabel() {
|
||||
return this.operatorWebsite ? this.getDomain(this.operatorWebsite) : null;
|
||||
}
|
||||
|
||||
get operatorWikidata() {
|
||||
const val = this.tags['operator:wikidata'];
|
||||
if (!val) return null;
|
||||
return (
|
||||
val
|
||||
.split(';')
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean)[0] || null
|
||||
);
|
||||
}
|
||||
|
||||
get operatorWikipedia() {
|
||||
const val = this.tags['operator:wikipedia'];
|
||||
if (!val) return null;
|
||||
return (
|
||||
val
|
||||
.split(';')
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean)[0] || null
|
||||
);
|
||||
}
|
||||
|
||||
get operatorWikipediaUrl() {
|
||||
const val = this.operatorWikipedia;
|
||||
if (!val) return null;
|
||||
const match = val.match(/^([a-z-]+):(.+)$/i);
|
||||
if (match) {
|
||||
return `https://${match[1]}.wikipedia.org/wiki/${match[2]}`;
|
||||
}
|
||||
return `https://wikipedia.org/wiki/${val}`;
|
||||
}
|
||||
|
||||
get showOperator() {
|
||||
return !!(
|
||||
this.operatorNames ||
|
||||
this.operatorWebsite ||
|
||||
this.operatorWikidata ||
|
||||
this.operatorWikipedia
|
||||
);
|
||||
}
|
||||
|
||||
get cuisine() {
|
||||
if (!this.tags.cuisine) return null;
|
||||
return this.tags.cuisine
|
||||
@@ -490,6 +554,45 @@ export default class PlaceDetails extends Component {
|
||||
</p>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showOperator}}
|
||||
<p class="content-with-icon">
|
||||
<Icon @name="briefcase" @title="Operator" />
|
||||
<span>
|
||||
{{this.operatorNames}}
|
||||
{{#if this.operatorWebsite}}
|
||||
<br />
|
||||
<a
|
||||
href={{this.operatorWebsite}}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{{this.operatorWebsiteLabel}}
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if this.operatorWikidata}}
|
||||
<br />
|
||||
<a
|
||||
href="https://www.wikidata.org/wiki/{{this.operatorWikidata}}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Wikidata
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if this.operatorWikipedia}}
|
||||
<br />
|
||||
<a
|
||||
href={{this.operatorWikipediaUrl}}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Wikipedia
|
||||
</a>
|
||||
{{/if}}
|
||||
</span>
|
||||
</p>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.phone}}
|
||||
<p class="content-with-icon">
|
||||
<Icon @name="phone" @title="Phone" />
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import activity from 'feather-icons/dist/icons/activity.svg?raw';
|
||||
import arrowLeft from 'feather-icons/dist/icons/arrow-left.svg?raw';
|
||||
import bookmark from 'feather-icons/dist/icons/bookmark.svg?raw';
|
||||
import briefcase from 'feather-icons/dist/icons/briefcase.svg?raw';
|
||||
import featherCamera from 'feather-icons/dist/icons/camera.svg?raw';
|
||||
import checkSquare from 'feather-icons/dist/icons/check-square.svg?raw';
|
||||
import chevronLeft from 'feather-icons/dist/icons/chevron-left.svg?raw';
|
||||
@@ -182,6 +183,7 @@ const ICONS = {
|
||||
'boxing-glove-up': boxingGloveUp,
|
||||
'burger-and-drink-cup-with-straw': burgerAndDrinkCupWithStraw,
|
||||
bridge,
|
||||
briefcase,
|
||||
bus,
|
||||
camera,
|
||||
'feather-camera': featherCamera,
|
||||
|
||||
@@ -457,4 +457,112 @@ module('Integration | Component | place-details', function (hooks) {
|
||||
|
||||
assert.dom('.place-payment-methods').doesNotExist();
|
||||
});
|
||||
|
||||
test('it renders the operator name with a briefcase icon', async function (assert) {
|
||||
const place = {
|
||||
title: 'Operated Cafe',
|
||||
osmTags: {
|
||||
operator: 'Le Méridien',
|
||||
},
|
||||
};
|
||||
|
||||
await render(<template><PlaceDetails @place={{place}} /></template>);
|
||||
|
||||
const operatorBlock = this.element.querySelector(
|
||||
'.meta-info .content-with-icon span.icon[title="Operator"]'
|
||||
);
|
||||
assert.ok(operatorBlock, 'Operator block is rendered');
|
||||
|
||||
const row = operatorBlock.closest('.content-with-icon');
|
||||
assert.dom(row).includesText('Le Méridien');
|
||||
});
|
||||
|
||||
test('it splits semicolon-separated operators onto separate lines', async function (assert) {
|
||||
const place = {
|
||||
title: 'Multi Operator Stop',
|
||||
osmTags: {
|
||||
operator: 'De Lijn;TEC',
|
||||
},
|
||||
};
|
||||
|
||||
await render(<template><PlaceDetails @place={{place}} /></template>);
|
||||
|
||||
const row = this.element
|
||||
.querySelector('span.icon[title="Operator"]')
|
||||
.closest('.content-with-icon');
|
||||
|
||||
assert.dom(row).includesText('De Lijn');
|
||||
assert.dom(row).includesText('TEC');
|
||||
assert.true(
|
||||
row.querySelector('span:not(.icon)').innerHTML.includes('<br>'),
|
||||
'Operators are separated by a line break'
|
||||
);
|
||||
});
|
||||
|
||||
test('it suppresses invalid operator values of yes and no', async function (assert) {
|
||||
const place = {
|
||||
title: 'Invalid Operator',
|
||||
osmTags: {
|
||||
operator: 'yes',
|
||||
},
|
||||
};
|
||||
|
||||
await render(<template><PlaceDetails @place={{place}} /></template>);
|
||||
|
||||
assert
|
||||
.dom('span.icon[title="Operator"]')
|
||||
.doesNotExist('Operator block is hidden for operator=yes');
|
||||
});
|
||||
|
||||
test('it renders operator wikidata and website links', async function (assert) {
|
||||
const place = {
|
||||
title: 'Linked Operator',
|
||||
osmTags: {
|
||||
operator: 'Flixbus',
|
||||
'operator:wikidata': 'Q15712278',
|
||||
'operator:website': 'https://www.flixbus.com/',
|
||||
'operator:wikipedia': 'en:FlixBus',
|
||||
},
|
||||
};
|
||||
|
||||
await render(<template><PlaceDetails @place={{place}} /></template>);
|
||||
|
||||
const row = this.element
|
||||
.querySelector('span.icon[title="Operator"]')
|
||||
.closest('.content-with-icon');
|
||||
|
||||
const wikidataLink = row.querySelector(
|
||||
'a[href="https://www.wikidata.org/wiki/Q15712278"]'
|
||||
);
|
||||
assert.ok(wikidataLink, 'Wikidata link is rendered');
|
||||
assert.dom(wikidataLink).hasText('Wikidata');
|
||||
|
||||
const websiteLink = row.querySelector('a[href="https://www.flixbus.com/"]');
|
||||
assert.ok(websiteLink, 'Operator website link is rendered');
|
||||
assert.dom(websiteLink).hasText('www.flixbus.com');
|
||||
|
||||
const wikipediaLink = row.querySelector(
|
||||
'a[href="https://en.wikipedia.org/wiki/FlixBus"]'
|
||||
);
|
||||
assert.ok(wikipediaLink, 'Operator Wikipedia link is rendered');
|
||||
assert.dom(wikipediaLink).hasText('Wikipedia');
|
||||
});
|
||||
|
||||
test('it renders the operator block when only operator subkeys are present', async function (assert) {
|
||||
const place = {
|
||||
title: 'Subkey Only',
|
||||
osmTags: {
|
||||
'operator:wikidata': 'Q42',
|
||||
},
|
||||
};
|
||||
|
||||
await render(<template><PlaceDetails @place={{place}} /></template>);
|
||||
|
||||
assert
|
||||
.dom('span.icon[title="Operator"]')
|
||||
.exists('Operator block is rendered from subkeys alone');
|
||||
assert
|
||||
.dom('a[href="https://www.wikidata.org/wiki/Q42"]')
|
||||
.exists('Wikidata link is rendered');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user